@elliemae/ds-codemods 3.26.0-next.5 → 3.26.0-next.7
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 +13 -0
- package/bin/cli/code-mods/command-arguments/getDeprecatedUsageReportQuestions.mjs +31 -0
- package/bin/cli/code-mods/command-arguments/promptDeprecatedPackages.mjs +1 -1
- package/bin/cli/code-mods/command-logics/check-deprecated-packages/logResults.mjs +1 -1
- package/bin/cli/code-mods/command-logics/deprecated-components-usage-report/index.mjs +36 -0
- package/bin/cli/code-mods/command-logics/deprecated-components-usage-report/regexp.mjs +55 -0
- package/bin/cli/code-mods/command-logics/deprecated-components-usage-report/utils.mjs +256 -0
- package/bin/cli/code-mods/command-logics/execute-commands-map.mjs +4 -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 +332 -0
- package/bin/cli/constants/index.mjs +7 -0
- package/bin/cli/utils/File Matching.md +83 -0
- package/bin/cli/utils/debugHelpers.mjs +58 -0
- package/bin/cli/utils/filepathsMatchers.mjs +198 -0
- package/bin/cli/utils/index.mjs +5 -5
- package/package.json +1 -1
- package/bin/cli/code-mods/command-logics/check-deprecated-packages/constants.mjs +0 -87
- package/bin/cli/utils/generatePathFromCurrentFolder.mjs +0 -11
|
@@ -11,6 +11,14 @@ function parseArgumentsIntoOptions(rawArgs) {
|
|
|
11
11
|
'--projectFolderPath': String,
|
|
12
12
|
'--ignorePackagesGlobPattern': String,
|
|
13
13
|
'--ignoreFilesGlobPattern': String,
|
|
14
|
+
'--outputPath': String,
|
|
15
|
+
'--startingDirPath': String,
|
|
16
|
+
'--gitIgnorePath': String,
|
|
17
|
+
// CLI flag, not available as a question -> Boolean are not supported by the combo of arg and (dynamic) inquirer.
|
|
18
|
+
// it's impossible to determine if the user has passed the flag or not because
|
|
19
|
+
// for arg Boolean are either true or undefined.
|
|
20
|
+
'--debug': Boolean,
|
|
21
|
+
'--fileExtensions': String,
|
|
14
22
|
},
|
|
15
23
|
{
|
|
16
24
|
argv: rawArgs.slice(2),
|
|
@@ -23,6 +31,11 @@ function parseArgumentsIntoOptions(rawArgs) {
|
|
|
23
31
|
projectFolderPath: args['--projectFolderPath'],
|
|
24
32
|
ignorePackagesGlobPattern: args['--ignorePackagesGlobPattern'],
|
|
25
33
|
ignoreFilesGlobPattern: args['--ignoreFilesGlobPattern'],
|
|
34
|
+
outputPath: args['--outputPath'],
|
|
35
|
+
startingDirPath: args['--startingDirPath'],
|
|
36
|
+
debug: args['--debug'], // CLI flag, not available as a question.
|
|
37
|
+
fileExtensions: args['--fileExtensions'], // CLI flag, not available as a question.
|
|
38
|
+
gitIgnorePath: args['--gitIgnorePath'],
|
|
26
39
|
script: args._[0],
|
|
27
40
|
};
|
|
28
41
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const getDeprecatedUsageReportQuestions = (originalOptions) => {
|
|
2
|
+
const extraOptions = [];
|
|
3
|
+
const { outputPath, startingDirPath, gitIgnorePath } = originalOptions;
|
|
4
|
+
if (!outputPath && outputPath !== '') {
|
|
5
|
+
extraOptions.push({
|
|
6
|
+
type: 'input',
|
|
7
|
+
name: 'outputPath',
|
|
8
|
+
message: 'path to the .csv file to write the report to',
|
|
9
|
+
default: './dimsum-deprecated-components-usage-report.csv',
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
if (!startingDirPath && startingDirPath !== '') {
|
|
13
|
+
extraOptions.push({
|
|
14
|
+
type: 'input',
|
|
15
|
+
name: 'startingDirPath',
|
|
16
|
+
message: 'Please provide the project root directory (where node_modules folder lives)',
|
|
17
|
+
default: './',
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
if (!gitIgnorePath && gitIgnorePath !== '') {
|
|
21
|
+
extraOptions.push({
|
|
22
|
+
type: 'input',
|
|
23
|
+
name: 'gitIgnorePath',
|
|
24
|
+
message: 'Please provide the path to the .gitignore file',
|
|
25
|
+
default: './.gitignore',
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
// --debug is a CLI flag, not available as a question.
|
|
29
|
+
|
|
30
|
+
return extraOptions;
|
|
31
|
+
};
|
|
@@ -4,7 +4,7 @@ export const getDeprecatedPackagesQuestions = (originalOptions) => {
|
|
|
4
4
|
if (!cwd && cwd !== '') {
|
|
5
5
|
extraOptions.push({
|
|
6
6
|
type: 'input',
|
|
7
|
-
name: '
|
|
7
|
+
name: 'cwd',
|
|
8
8
|
message: 'Please provide the project root directory (where node_modules folder lives)',
|
|
9
9
|
default: './',
|
|
10
10
|
});
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
brightCyanStr,
|
|
7
7
|
whiteStr,
|
|
8
8
|
} from '../../../utils/CLI_COLORS.mjs';
|
|
9
|
-
import { DEPRECATED_PACKAGES } from '
|
|
9
|
+
import { DEPRECATED_PACKAGES } from '../../../constants/deprecatedPackages.mjs';
|
|
10
10
|
|
|
11
11
|
const logDeprecatedComponent = ({ name, solution, severity, confluence }) => {
|
|
12
12
|
const [sevDesc, sevLevel] = severity;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import { filePathsWithGitIgnore } from '../../../utils/filepathsMatchers.mjs';
|
|
3
|
+
import { generateCsvRows, checkFileForDeprecatedComponents } from './utils.mjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* generates a report of deprecated components usage
|
|
7
|
+
* @param {object} options - options *
|
|
8
|
+
* @param {string} options.outputPath - path to the .csv file to write the report to
|
|
9
|
+
* @param {string} options.startingDirPath - path to the project root directory (where node_modules folder lives)
|
|
10
|
+
* @param {string} options.gitIgnorePath - path to the .gitignore file
|
|
11
|
+
* @param {boolean} options.debug - debug flag
|
|
12
|
+
*
|
|
13
|
+
* @returns {void}
|
|
14
|
+
*/
|
|
15
|
+
export const deprecatedComponentsUsageReport = (options) => {
|
|
16
|
+
const { outputPath } = options;
|
|
17
|
+
|
|
18
|
+
const filesToParse = filePathsWithGitIgnore(options);
|
|
19
|
+
// we have a lot more info than the one we use in the csv,
|
|
20
|
+
// in case we want to expand it in the future we can store it in the map...
|
|
21
|
+
// const problematicFiles = new Map();
|
|
22
|
+
|
|
23
|
+
const csvRows = ['Legacy Component,Legacy Package,Filename,File Path'];
|
|
24
|
+
filesToParse.forEach((filePath) => {
|
|
25
|
+
const fileReport = checkFileForDeprecatedComponents(filePath);
|
|
26
|
+
if (fileReport.isFileProblematic) {
|
|
27
|
+
// we have a lot more info than the one we use in the csv,
|
|
28
|
+
// in case we want to expand it in the future we can store it in the map...
|
|
29
|
+
// problematicFiles.set(filePath, fileReport);
|
|
30
|
+
csvRows.push(...generateCsvRows(fileReport, filePath));
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
fs.writeFileSync(outputPath, csvRows.join('\n'));
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default deprecatedComponentsUsageReport;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// eslint-disable-next-line max-len, max-len
|
|
2
|
+
// /^import\s*((?:[\S]*(?:\s*as\s*[\S]*)?(?:\s*,\s*(?:(?:[\S]*\s*as\s*[\S]*)|(?:\{(?:[\s\S](?!{))*?\})))?)|(?:\{(?:[\s\S](?!{))*?\}))\s*from\s*['"](@elliemae\/ds-\S+)['"]/gms
|
|
3
|
+
/*
|
|
4
|
+
reg-exp divided into units for readability:
|
|
5
|
+
^import\s* - matches "import" followed by any number of spaces,
|
|
6
|
+
constraints the match to the beginning of the line
|
|
7
|
+
( - start of the first capturing group
|
|
8
|
+
// this one captures:
|
|
9
|
+
- the default import (e.g. import Button from 'wherever')
|
|
10
|
+
- the named import (e.g. import Button as Btn from 'wherever')
|
|
11
|
+
- the default import followed by a comma (e.g. import Button, { Input } from 'wherever')
|
|
12
|
+
(?:
|
|
13
|
+
[\S]* - matches any non-whitespace character any number of times
|
|
14
|
+
this is meant to catch the default import (e.g. import Button from 'wherever')
|
|
15
|
+
(?:\s*as\s*[\S]*)? - optionally matches the named import (e.g. import Button as Btn from 'wherever')
|
|
16
|
+
|
|
17
|
+
(?: - optionally matches imports with comma after them
|
|
18
|
+
(e.g. import Button, { Input } from 'wherever')
|
|
19
|
+
\s*,\s* - matches a comma, with any number of spaces before and after it
|
|
20
|
+
- in ESM after a comma you have a named default import or a destructured import...
|
|
21
|
+
(?:
|
|
22
|
+
(?:[\S]*\s*as\s*[\S]*) - the catching of "default" imports as above,
|
|
23
|
+
no optional named import because after comma you can't repeat the default import
|
|
24
|
+
|
|
|
25
|
+
(?:\{ - between curly braces START
|
|
26
|
+
(?:[\s\S] - anything (including new lines)
|
|
27
|
+
(?!{) - as long as it's not followed by another curly brace
|
|
28
|
+
(avoid matching import from other packages followed by import from @elliemae/ds-* )
|
|
29
|
+
)*?
|
|
30
|
+
\}) - between curly braces END
|
|
31
|
+
)
|
|
32
|
+
)?
|
|
33
|
+
)
|
|
34
|
+
| - OR
|
|
35
|
+
// this one captures:
|
|
36
|
+
- the destructured import (e.g. import { Button } from 'wherever')
|
|
37
|
+
(?:\{ - between curly braces START
|
|
38
|
+
(?:[\s\S] - anything (including new lines)
|
|
39
|
+
(?!{) - as long as it's not followed by another curly brace
|
|
40
|
+
(avoid matching import from other packages followed by import from @elliemae/ds-* )
|
|
41
|
+
)*?
|
|
42
|
+
\}) - between curly braces END
|
|
43
|
+
|
|
44
|
+
)
|
|
45
|
+
\s*from\s* - matches "from" with any number of spaces before and after it
|
|
46
|
+
- matches a string between single or double quotes that starts with "@elliemae/ds-*"
|
|
47
|
+
['"]
|
|
48
|
+
(@elliemae\/ds-\S+)
|
|
49
|
+
['"]
|
|
50
|
+
|
|
51
|
+
https://regex101.com/r/7BsJPF/1
|
|
52
|
+
*/
|
|
53
|
+
export const dimsumImportStatementsRegExp =
|
|
54
|
+
// eslint-disable-next-line max-len
|
|
55
|
+
/^import\s*((?:[\S]*(?:\s*as\s*[\S]*)?(?:\s*,\s*(?:(?:[\S]*\s*as\s*[\S]*)|(?:\{(?:[\s\S](?!{))*?\})))?)|(?:\{(?:[\s\S](?!{))*?\}))\s*from\s*['"](@elliemae\/ds-\S+)['"]/gms;
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/* eslint-disable max-lines */
|
|
2
|
+
/* eslint-disable max-statements */
|
|
3
|
+
/* eslint-disable max-len */
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import { dimsumImportStatementsRegExp } from './regexp.mjs';
|
|
6
|
+
import {
|
|
7
|
+
LEGACY_WITH_NEW_SMALL_MIGRATION_EFFORT,
|
|
8
|
+
LEGACY_WITH_NEW_MEDIUM_MIGRATION_EFFORT,
|
|
9
|
+
LEGACY_WITH_NEW_LARGE_MIGRATION_EFFORT,
|
|
10
|
+
DISMISSED_WITH_EXAMPLE,
|
|
11
|
+
} from '../../../constants/index.mjs';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {typeof LEGACY_WITH_NEW_SMALL_MIGRATION_EFFORT
|
|
15
|
+
* | typeof LEGACY_WITH_NEW_MEDIUM_MIGRATION_EFFORT
|
|
16
|
+
* | typeof LEGACY_WITH_NEW_LARGE_MIGRATION_EFFORT} deprecatedEffortMap
|
|
17
|
+
* @typedef {deprecatedEffortMap[number]} deprecatedEffortMapEntry
|
|
18
|
+
* @typedef {typeof DISMISSED_WITH_EXAMPLE[number]} dismissedWithExampleEntry
|
|
19
|
+
*
|
|
20
|
+
* @typedef { object } regexpInfo
|
|
21
|
+
* @property { string } regexpInfo.fullMatch
|
|
22
|
+
* @property { string } regexpInfo.importedComponent
|
|
23
|
+
* @property { string } regexpInfo.packageName
|
|
24
|
+
*
|
|
25
|
+
* @typedef {object} matchSharedPropieties
|
|
26
|
+
* @property {regexpInfo} matchSharedPropieties.regexpInfo
|
|
27
|
+
*
|
|
28
|
+
* @typedef { object } matchWithEffortEntryPropeties
|
|
29
|
+
* @property { deprecatedEffortMapEntry } matchWithEffortEntryPropeties.matchingEffortMetainfo
|
|
30
|
+
*
|
|
31
|
+
* @typedef { object } matchDeprecatedEntryPropeties
|
|
32
|
+
* @property { dismissedWithExampleEntry } matchDeprecatedEntryPropeties.matchingEffortMetainfo
|
|
33
|
+
*
|
|
34
|
+
* @typedef {matchSharedPropieties & matchWithEffortEntryPropeties} matchWithEffortMetaInfo
|
|
35
|
+
* @typedef {matchSharedPropieties & matchDeprecatedEntryPropeties} matchDeprecatedWithExample
|
|
36
|
+
*
|
|
37
|
+
* @typedef {matchWithEffortMetaInfo | matchDeprecatedWithExample} matchedEffortMapValue
|
|
38
|
+
*
|
|
39
|
+
* @typedef {Map<string, matchedEffortMapValue>} matchedEffortMap
|
|
40
|
+
*
|
|
41
|
+
* @typedef {object} fileReport
|
|
42
|
+
* @property {boolean} fileReport.isFileProblematic
|
|
43
|
+
* @property {matchWithEffortMetaInfo} fileReport.deprecatedSmallEffort
|
|
44
|
+
* @property {matchWithEffortMetaInfo} fileReport.deprecatedMediumEffort
|
|
45
|
+
* @property {matchWithEffortMetaInfo} fileReport.deprecatedLargeEffort
|
|
46
|
+
* @property {matchDeprecatedWithExample} fileReport.deprecatedDismissed
|
|
47
|
+
*
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
// we generate some supporting data structures to make the matching more efficient from the existing constants...
|
|
51
|
+
// we do this runtime because we want to avoid having to maintain this manually and this is a one time cost runtime wise with the cache...
|
|
52
|
+
const runtimeDeprecatedDSReportCache = new Map();
|
|
53
|
+
const getSupportingDataStructures = (deprecatedEffortMap, effortCacheUID) => {
|
|
54
|
+
if (runtimeDeprecatedDSReportCache.has(effortCacheUID)) {
|
|
55
|
+
return runtimeDeprecatedDSReportCache.get(effortCacheUID);
|
|
56
|
+
}
|
|
57
|
+
// we generate some supporting data structures to make the matching more efficient from the existing constants...
|
|
58
|
+
const effortsSamePackageList = [];
|
|
59
|
+
const effortsSamePackageOldComponentsList = [];
|
|
60
|
+
const effortsDifferentPackageList = [];
|
|
61
|
+
|
|
62
|
+
deprecatedEffortMap.forEach((DeprecatedPackageMeta) => {
|
|
63
|
+
const { oldPackage, component, newPackage } = DeprecatedPackageMeta;
|
|
64
|
+
if (oldPackage === newPackage) {
|
|
65
|
+
if (!effortsSamePackageList.includes(oldPackage)) effortsSamePackageList.push(oldPackage);
|
|
66
|
+
if (!effortsSamePackageOldComponentsList.includes(component)) effortsSamePackageOldComponentsList.push(component);
|
|
67
|
+
} else {
|
|
68
|
+
if (!effortsDifferentPackageList.includes(oldPackage)) effortsDifferentPackageList.push(oldPackage);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
runtimeDeprecatedDSReportCache.set(effortCacheUID, {
|
|
73
|
+
effortsSamePackageList,
|
|
74
|
+
effortsSamePackageOldComponentsList,
|
|
75
|
+
effortsDifferentPackageList,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
return runtimeDeprecatedDSReportCache.get(effortCacheUID);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* given a list of import statements, it checks if any of them is deprecated based on the constants provided
|
|
83
|
+
* @param {string[][]} importStatements - list of import statements to check generated by the regex
|
|
84
|
+
* @param {LEGACY_WITH_NEW_SMALL_MIGRATION_EFFORT} deprecatedEffortMap - list of constants to check against
|
|
85
|
+
* @param {string} effortCacheUID - a string to identify the effort level (small, medium, large) in the cache if existing
|
|
86
|
+
* @returns {Map<string, matchWithEffortMetaInfo>} - a map of matched statements
|
|
87
|
+
*/
|
|
88
|
+
const checkDeprecatedEffort = (importStatements, deprecatedEffortMap, effortCacheUID = '') => {
|
|
89
|
+
const { effortsSamePackageList, effortsSamePackageOldComponentsList, effortsDifferentPackageList } =
|
|
90
|
+
getSupportingDataStructures(deprecatedEffortMap, effortCacheUID);
|
|
91
|
+
|
|
92
|
+
const matchedEfforts = new Map();
|
|
93
|
+
|
|
94
|
+
importStatements.forEach((importStatement) => {
|
|
95
|
+
const [fullMatch, importedComponent, packageName] = importStatement;
|
|
96
|
+
// first we check if we need to care about this package at all
|
|
97
|
+
// we do not care if the package is not in the list of packages that have small migration effort
|
|
98
|
+
// when the deprecation is in another package we know for sure that the statement is deprecated
|
|
99
|
+
const statementIsDeprecatedForSure =
|
|
100
|
+
effortsDifferentPackageList.findIndex((pck) => pck.includes(packageName)) !== -1;
|
|
101
|
+
// when the deprecation is in the same package we need to check if the component itself is deprecated
|
|
102
|
+
const statementIsPotentiallyDeprecated =
|
|
103
|
+
effortsSamePackageList.findIndex((pck) => pck.includes(packageName)) !== -1;
|
|
104
|
+
let isStatementActuallyDeprecated = false;
|
|
105
|
+
if (!statementIsDeprecatedForSure && statementIsPotentiallyDeprecated) {
|
|
106
|
+
effortsSamePackageOldComponentsList.forEach((deprecatedComponent) => {
|
|
107
|
+
if (importedComponent.includes(deprecatedComponent)) {
|
|
108
|
+
isStatementActuallyDeprecated = true;
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
const statementIsDeprecated = statementIsDeprecatedForSure || isStatementActuallyDeprecated;
|
|
113
|
+
if (statementIsDeprecated) {
|
|
114
|
+
// we iterate the deprecatedEffortMap again now that we know the statement is deprecated,
|
|
115
|
+
// we do this so we can reach the constant meta-informations (like the new package name, severityTags, etc...)
|
|
116
|
+
// technically this is O(n * m) where n is ~<10 and m is ~<50 so even the worst case is not that bad...
|
|
117
|
+
const matchingEffortMetainfoIndx = deprecatedEffortMap.findIndex((effortMetainfo) => {
|
|
118
|
+
if (statementIsDeprecatedForSure) {
|
|
119
|
+
return effortMetainfo.oldPackage === packageName;
|
|
120
|
+
}
|
|
121
|
+
return importedComponent.includes(effortMetainfo.component);
|
|
122
|
+
});
|
|
123
|
+
matchedEfforts.set(fullMatch, {
|
|
124
|
+
regexpInfo: { fullMatch, importedComponent, packageName },
|
|
125
|
+
matchingEffortMetainfo: deprecatedEffortMap[matchingEffortMetainfoIndx],
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
return matchedEfforts;
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* given a list of import statements, it checks if any of them is a low effort deprecation
|
|
133
|
+
* @param {string[][]} importStatements - list of import statements to check generated by the regex
|
|
134
|
+
* @returns {Map<string, matchWithEffortMetaInfo>} - a map of matched statements
|
|
135
|
+
*/
|
|
136
|
+
export const checkDeprecatedSmallEffort = (importStatements) =>
|
|
137
|
+
checkDeprecatedEffort(importStatements, LEGACY_WITH_NEW_SMALL_MIGRATION_EFFORT, 'small');
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* given a list of import statements, it checks if any of them is a medium effort deprecation
|
|
141
|
+
* @param {string[][]} importStatements - list of import statements to check generated by the regex
|
|
142
|
+
* @returns {Map<string, matchWithEffortMetaInfo>} - a map of matched statements
|
|
143
|
+
*/
|
|
144
|
+
export const checkDeprecatedMediumEffort = (importStatements) =>
|
|
145
|
+
checkDeprecatedEffort(importStatements, LEGACY_WITH_NEW_MEDIUM_MIGRATION_EFFORT, 'medium');
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* given a list of import statements, it checks if any of them is a large effort deprecation
|
|
149
|
+
* @param {string[][]} importStatements - list of import statements to check generated by the regex
|
|
150
|
+
* @returns {Map<string, matchWithEffortMetaInfo>} - a map of matched statements
|
|
151
|
+
*/
|
|
152
|
+
export const checkDeprecatedLargeEffort = (importStatements) =>
|
|
153
|
+
checkDeprecatedEffort(importStatements, LEGACY_WITH_NEW_LARGE_MIGRATION_EFFORT, 'large');
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* given a list of import statements, it checks if any of them is a large effort deprecation
|
|
157
|
+
* @param {string[][]} importStatements - list of import statements to check generated by the regex
|
|
158
|
+
* @returns {Map<string, matchDeprecatedWithExample>} - a map of matched statements
|
|
159
|
+
*/
|
|
160
|
+
export const checkDeprecatedDismissed = (importStatements) => {
|
|
161
|
+
const matchedEfforts = new Map();
|
|
162
|
+
importStatements.forEach((importStatement) => {
|
|
163
|
+
const [fullMatch, importedComponent, packageName] = importStatement;
|
|
164
|
+
const matchingEffortMetainfoIndx = DISMISSED_WITH_EXAMPLE.findIndex((effortMetainfo) =>
|
|
165
|
+
importedComponent.includes(effortMetainfo.component),
|
|
166
|
+
);
|
|
167
|
+
if (matchingEffortMetainfoIndx === -1) return;
|
|
168
|
+
matchedEfforts.set(fullMatch, {
|
|
169
|
+
regexpInfo: { fullMatch, importedComponent, packageName },
|
|
170
|
+
matchingEffortMetainfo: DISMISSED_WITH_EXAMPLE[matchingEffortMetainfoIndx],
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
return matchedEfforts;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* given a file path, it checks if the file contains any deprecated components
|
|
178
|
+
* @param {string} filePath - path to the file to check
|
|
179
|
+
* @returns {fileReport} - a report of the file
|
|
180
|
+
*/
|
|
181
|
+
export const checkFileForDeprecatedComponents = (filePath) => {
|
|
182
|
+
const fileAsString = fs.readFileSync(filePath, 'utf8');
|
|
183
|
+
const matches = fileAsString.matchAll(dimsumImportStatementsRegExp);
|
|
184
|
+
const importStatements = [...matches]; // convert iterator to array - matchAll returns an iterator...
|
|
185
|
+
const deprecatedSmallEffort = checkDeprecatedSmallEffort(importStatements);
|
|
186
|
+
const deprecatedMediumEffort = checkDeprecatedMediumEffort(importStatements);
|
|
187
|
+
const deprecatedLargeEffort = checkDeprecatedLargeEffort(importStatements);
|
|
188
|
+
const deprecatedDismissed = checkDeprecatedDismissed(importStatements);
|
|
189
|
+
|
|
190
|
+
const isFileProblematic =
|
|
191
|
+
deprecatedSmallEffort.size > 0 ||
|
|
192
|
+
deprecatedMediumEffort.size > 0 ||
|
|
193
|
+
deprecatedLargeEffort.size > 0 ||
|
|
194
|
+
deprecatedDismissed.size > 0;
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
isFileProblematic,
|
|
198
|
+
deprecatedSmallEffort,
|
|
199
|
+
deprecatedMediumEffort,
|
|
200
|
+
deprecatedLargeEffort,
|
|
201
|
+
deprecatedDismissed,
|
|
202
|
+
};
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Helper function to generate a csv row from a "matched effort" object
|
|
207
|
+
* @param {matchedEffortMapValue} matchedEffort
|
|
208
|
+
* @param {string} filePath
|
|
209
|
+
* @returns {string} - csv row
|
|
210
|
+
*/
|
|
211
|
+
const generateCsvRowFromMatchedEffort = (matchedEffort, filePath) => {
|
|
212
|
+
const { matchingEffortMetainfo } = matchedEffort;
|
|
213
|
+
// matchingEffortMetainfo type is deprecatedEffortMapEntry | dismissedWithExampleEntry
|
|
214
|
+
// they share 'component' and 'oldPackage' properties and for the current use case we only care about those
|
|
215
|
+
// if we want to expand the csv in the future we have to take care of this difference in the types
|
|
216
|
+
const { component, oldPackage } = matchingEffortMetainfo;
|
|
217
|
+
const filename = filePath.split('/').pop();
|
|
218
|
+
const cwdFolders = process.cwd().split('/');
|
|
219
|
+
const pathFolders = filePath.split('/');
|
|
220
|
+
const latestMatchingFolderIndx = pathFolders.findIndex((folder, indx) => folder !== cwdFolders[indx]);
|
|
221
|
+
const filePathFolders = pathFolders.slice(latestMatchingFolderIndx);
|
|
222
|
+
const reducedFilePath = filePathFolders.join('/');
|
|
223
|
+
return `${component},${oldPackage},${filename},${reducedFilePath}`;
|
|
224
|
+
};
|
|
225
|
+
/**
|
|
226
|
+
* Helper function to generate a list of csv rows from a given file report
|
|
227
|
+
* @param {fileReport} fileReport
|
|
228
|
+
* @param {string} filePath
|
|
229
|
+
* @returns {string[]} - list of csv rows
|
|
230
|
+
*/
|
|
231
|
+
export const generateCsvRows = (fileReport, filePath) => {
|
|
232
|
+
const { deprecatedSmallEffort, deprecatedMediumEffort, deprecatedLargeEffort, deprecatedDismissed } = fileReport;
|
|
233
|
+
// csv structure = 'Legacy Component,Legacy Package,Filename,File Path'
|
|
234
|
+
// Legacy Component -> matchingEffortMetainfo.component
|
|
235
|
+
// Legacy Package -> matchingEffortMetainfo.oldPackage
|
|
236
|
+
// Filename -> filePath last part
|
|
237
|
+
// File Path -> filePath starting from latest matching folder from cwd
|
|
238
|
+
// (if cwd is /a/b/c/d/ and file path /a/b/1/2/3/file.tsx -> b/1/2/3/file.tsx)
|
|
239
|
+
// this is not a perfect "detect the root" solution, but should work in our CI/CD pipeline
|
|
240
|
+
const csvRows = [];
|
|
241
|
+
deprecatedSmallEffort.forEach((effort) => {
|
|
242
|
+
csvRows.push(generateCsvRowFromMatchedEffort(effort, filePath));
|
|
243
|
+
});
|
|
244
|
+
deprecatedMediumEffort.forEach((effort) => {
|
|
245
|
+
csvRows.push(generateCsvRowFromMatchedEffort(effort, filePath));
|
|
246
|
+
});
|
|
247
|
+
deprecatedLargeEffort.forEach((effort) => {
|
|
248
|
+
csvRows.push(generateCsvRowFromMatchedEffort(effort, filePath));
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
deprecatedDismissed.forEach((effort) => {
|
|
252
|
+
csvRows.push(generateCsvRowFromMatchedEffort(effort, filePath));
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
return csvRows;
|
|
256
|
+
};
|
|
@@ -3,6 +3,7 @@ import { checkPackagesInconsistencies } from './check-packages-inconsistencies/i
|
|
|
3
3
|
import { checkMissingPackages } from './check-missing-packages/index.mjs';
|
|
4
4
|
import { checkDeprecatedPackages } from './check-deprecated-packages/index.mjs';
|
|
5
5
|
import { helpMigrateToV3 } from './help-migrate-to-v3/index.mjs';
|
|
6
|
+
import { deprecatedComponentsUsageReport } from './deprecated-components-usage-report/index.mjs';
|
|
6
7
|
import { COMMANDS } from '../commands.mjs';
|
|
7
8
|
|
|
8
9
|
export async function executeCommandsMap(args, options) {
|
|
@@ -25,6 +26,9 @@ export async function executeCommandsMap(args, options) {
|
|
|
25
26
|
case COMMANDS.HELP_MIGRATE_TO_V3:
|
|
26
27
|
helpMigrateToV3(options);
|
|
27
28
|
break;
|
|
29
|
+
case COMMANDS.DEPRECATED_PACKAGES_USAGE_REPORT:
|
|
30
|
+
deprecatedComponentsUsageReport(options);
|
|
31
|
+
break;
|
|
28
32
|
default:
|
|
29
33
|
break;
|
|
30
34
|
}
|
|
@@ -5,6 +5,7 @@ export const COMMANDS = {
|
|
|
5
5
|
CHECK_PACKAGES_INCONSISTENCIES: 'check-packages-inconsistencies',
|
|
6
6
|
CHECK_MISSING_PACKAGES: 'check-missing-packages',
|
|
7
7
|
HELP_MIGRATE_TO_V3: 'help-migrate-to-v3',
|
|
8
|
+
DEPRECATED_PACKAGES_USAGE_REPORT: 'deprecated-components-usage-report',
|
|
8
9
|
EXIT: 'exit',
|
|
9
10
|
};
|
|
10
11
|
|
|
@@ -5,6 +5,7 @@ import { getPackagesInconsistenciesQuestions } from './command-arguments/promptC
|
|
|
5
5
|
import { getDeprecatedPackagesQuestions } from './command-arguments/promptDeprecatedPackages.mjs';
|
|
6
6
|
import { getHelpMigrateToV3Questions } from './command-arguments/promptHelpMigrateToV3.mjs';
|
|
7
7
|
import { getMissingPackagesQuestions } from './command-arguments/promptMissingPackages.mjs';
|
|
8
|
+
import { getDeprecatedUsageReportQuestions } from './command-arguments/getDeprecatedUsageReportQuestions.mjs';
|
|
8
9
|
|
|
9
10
|
async function promptForMissingScriptSpecificOptions({ originalOptions, promptOptions }) {
|
|
10
11
|
const script = originalOptions.script || promptOptions.script;
|
|
@@ -26,6 +27,9 @@ async function promptForMissingScriptSpecificOptions({ originalOptions, promptOp
|
|
|
26
27
|
case COMMANDS.HELP_MIGRATE_TO_V3:
|
|
27
28
|
scriptQuestions.push(...getHelpMigrateToV3Questions(originalOptions));
|
|
28
29
|
break;
|
|
30
|
+
case COMMANDS.DEPRECATED_PACKAGES_USAGE_REPORT:
|
|
31
|
+
scriptQuestions.push(...getDeprecatedUsageReportQuestions(originalOptions));
|
|
32
|
+
break;
|
|
29
33
|
default:
|
|
30
34
|
break;
|
|
31
35
|
}
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
/* eslint-disable max-lines */
|
|
2
|
+
/**
|
|
3
|
+
* @typedef {object} SeverityTag
|
|
4
|
+
* @property {string} SeverityTag.name - the name of the tag
|
|
5
|
+
* @property {string} SeverityTag.severity - the severity of the tag
|
|
6
|
+
* @property {number} SeverityTag.severityInteger - the severity of the tag as an integer
|
|
7
|
+
* @property {string} SeverityTag.description - the description of the tag
|
|
8
|
+
*
|
|
9
|
+
* @typedef {object} DeprecatedPackageMatch
|
|
10
|
+
* @property {string} DeprecatedPackageMatch.component - the name of the LEGACY component
|
|
11
|
+
* @property {string} DeprecatedPackageMatch.oldPackage - the name of the LEGACY package
|
|
12
|
+
*
|
|
13
|
+
* @typedef {object} DeprecatedPackageWithEffortProperties
|
|
14
|
+
* @property {string} DeprecatedPackageWithEffortProperties.newComponent - the name of the NEW component
|
|
15
|
+
* @property {string} DeprecatedPackageWithEffortProperties.newPackage - the name of the NEW package
|
|
16
|
+
* @property {SeverityTag[]} DeprecatedPackageWithEffortProperties.tags - the tags associated with the component
|
|
17
|
+
*
|
|
18
|
+
* @typedef {DeprecatedPackageMatch & DeprecatedPackageWithEffortProperties} DeprecatedPackage
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const SEVERITY = {
|
|
22
|
+
NON_STARTER: ['Failing to upgrade will make the project impossible to start/use', 1],
|
|
23
|
+
KNOWN_ISSUES: [
|
|
24
|
+
'The component has unfixable known issues. Failing to upgrade means APP will have to live with those',
|
|
25
|
+
2,
|
|
26
|
+
],
|
|
27
|
+
FRAGILE_AND_UNMANTAINED: [
|
|
28
|
+
'Failing to upgrade will make the project fragile and any error will have to be addressed by the APP team',
|
|
29
|
+
3,
|
|
30
|
+
],
|
|
31
|
+
UNMANTAINED: ['Failing to upgrade will mean that any error will have to be addressed by the APP team', 4],
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const SOLUTIONS = {
|
|
35
|
+
REMOVED: 'the component has been regarded as supreflous and has hencheforth been removed in favor of examples',
|
|
36
|
+
NEW_VERSION_BREAKING: 'the component impedes further enhancments/bugfixes, a new version ex-novo has been created.',
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const DEPRECATED_PACKAGES = {
|
|
40
|
+
'@elliemae/ds-datagrids': {
|
|
41
|
+
name: 'data-grid',
|
|
42
|
+
solution: SOLUTIONS.NEW_VERSION_BREAKING,
|
|
43
|
+
severity: SEVERITY.KNOWN_ISSUES,
|
|
44
|
+
// TODO create relative confluence
|
|
45
|
+
confluence: 'https://confluence.elliemae.io/display/FEAE/Breaking+changes+-+Migration+steps+and+rationale',
|
|
46
|
+
},
|
|
47
|
+
'@elliemae/ds-date-picker': {
|
|
48
|
+
name: 'date-picker',
|
|
49
|
+
solution: SOLUTIONS.NEW_VERSION_BREAKING,
|
|
50
|
+
severity: SEVERITY.KNOWN_ISSUES,
|
|
51
|
+
// TODO create relative confluence
|
|
52
|
+
confluence: 'https://confluence.elliemae.io/display/FEAE/Breaking+changes+-+Migration+steps+and+rationale',
|
|
53
|
+
},
|
|
54
|
+
'@elliemae/ds-date-range-picker': {
|
|
55
|
+
name: 'date-range-picker',
|
|
56
|
+
solution: SOLUTIONS.NEW_VERSION_BREAKING,
|
|
57
|
+
severity: SEVERITY.KNOWN_ISSUES,
|
|
58
|
+
// TODO create relative confluence
|
|
59
|
+
confluence: 'https://confluence.elliemae.io/display/FEAE/Breaking+changes+-+Migration+steps+and+rationale',
|
|
60
|
+
},
|
|
61
|
+
'@elliemae/ds-date-time-picker': {
|
|
62
|
+
name: 'date-time-picker',
|
|
63
|
+
solution: SOLUTIONS.NEW_VERSION_BREAKING,
|
|
64
|
+
severity: SEVERITY.KNOWN_ISSUES,
|
|
65
|
+
// TODO create relative confluence
|
|
66
|
+
confluence: 'https://confluence.elliemae.io/display/FEAE/Breaking+changes+-+Migration+steps+and+rationale',
|
|
67
|
+
},
|
|
68
|
+
'@elliemae/ds-time-picker': {
|
|
69
|
+
name: 'time-picker',
|
|
70
|
+
solution: SOLUTIONS.NEW_VERSION_BREAKING,
|
|
71
|
+
severity: SEVERITY.KNOWN_ISSUES,
|
|
72
|
+
// TODO create relative confluence
|
|
73
|
+
confluence: 'https://confluence.elliemae.io/display/FEAE/Breaking+changes+-+Migration+steps+and+rationale',
|
|
74
|
+
},
|
|
75
|
+
'@elliemae/ds-button-group': {
|
|
76
|
+
name: 'button-group',
|
|
77
|
+
solution: SOLUTIONS.REMOVED,
|
|
78
|
+
severity: SEVERITY.NON_STARTER,
|
|
79
|
+
confluence: 'https://confluence.elliemae.io/display/FEAE/Button+Group+-+Dimsum+2.x',
|
|
80
|
+
},
|
|
81
|
+
'@elliemae/ds-card-array': {
|
|
82
|
+
name: 'card-array',
|
|
83
|
+
solution: SOLUTIONS.REMOVED,
|
|
84
|
+
severity: SEVERITY.NON_STARTER,
|
|
85
|
+
confluence: 'https://confluence.elliemae.io/display/FEAE/Card+Array+-+Dimsum+2.x',
|
|
86
|
+
},
|
|
87
|
+
'@elliemae/ds-form': {
|
|
88
|
+
subComponents: [
|
|
89
|
+
{
|
|
90
|
+
name: 'combobox',
|
|
91
|
+
solution: SOLUTIONS.NEW_VERSION_BREAKING,
|
|
92
|
+
severity: SEVERITY.FRAGILE_AND_UNMANTAINED,
|
|
93
|
+
confluence: 'https://confluence.elliemae.io/display/FEAE/Combo-box+-+Dimsum+2.x',
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
},
|
|
97
|
+
'@elliemae/ds-combobox-v1': {
|
|
98
|
+
subComponents: [
|
|
99
|
+
{
|
|
100
|
+
name: 'combobox',
|
|
101
|
+
solution: SOLUTIONS.NEW_VERSION_BREAKING,
|
|
102
|
+
severity: SEVERITY.FRAGILE_AND_UNMANTAINED,
|
|
103
|
+
confluence: 'https://confluence.elliemae.io/display/FEAE/Combo-box+-+Dimsum+2.x',
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/*********************************************************************************************
|
|
110
|
+
Those constant would be extremely hard to read if we "prettify" them with project rules...
|
|
111
|
+
I'm making a conscious decision to disable prettier and auto-formatting for this section of the code.
|
|
112
|
+
**********************************************************************************************/
|
|
113
|
+
/* eslint-disable max-len, prettier/prettier */
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* @type {object<string, SeverityTag>}
|
|
117
|
+
* @property {SeverityTag} SEVERITY_TAGS.A11Y_LIMITATIONS
|
|
118
|
+
* @property {SeverityTag} SEVERITY_TAGS.TOOLING_BLOCKER
|
|
119
|
+
* @property {SeverityTag} SEVERITY_TAGS.UNMANTAINED
|
|
120
|
+
* @property {SeverityTag} SEVERITY_TAGS.FRAGILE
|
|
121
|
+
* @property {SeverityTag} SEVERITY_TAGS.KNOWN_ISSUES
|
|
122
|
+
*/
|
|
123
|
+
/* prettier-ignore */
|
|
124
|
+
const SEVERITY_TAGS = {
|
|
125
|
+
// we don't expect to have any NON_STARTER, if we eventually do, we have to evaluate them manually
|
|
126
|
+
// { name: 'NON_STARTER', severity: "P1" },
|
|
127
|
+
A11Y_LIMITATIONS:{
|
|
128
|
+
name: 'A11Y_LIMITATIONS', severity: 'P2', severityInteger: 2,
|
|
129
|
+
description: 'The accessibility requirements are only available to the alternative solution' },
|
|
130
|
+
|
|
131
|
+
TOOLING_BLOCKER:{
|
|
132
|
+
name: 'TOOLING_BLOCKER', severity: 'P2', severityInteger: 2,
|
|
133
|
+
description:
|
|
134
|
+
'The component is based in third-party-packages that makes changing tools like jest, styled-compoents, etc... impossible' },
|
|
135
|
+
|
|
136
|
+
UNMANTAINED:{ name: 'UNMANTAINED', severity: 'P3', severityInteger: 3,
|
|
137
|
+
description:
|
|
138
|
+
'The component company-wise is low priority and has not received any meaningful attention in more than 8 months' },
|
|
139
|
+
|
|
140
|
+
FRAGILE:{ name: 'FRAGILE', severity: 'P3', severityInteger: 3,
|
|
141
|
+
description: 'The component is extremely fragile and not extensible' },
|
|
142
|
+
|
|
143
|
+
KNOWN_ISSUES:{ name: 'KNOWN_ISSUES', severity: 'P3', severityInteger: 3,
|
|
144
|
+
description: 'The component has known issues that cannot be fixed' },
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
/* prettier-ignore */
|
|
148
|
+
/**
|
|
149
|
+
* @type {DeprecatedPackage[]}
|
|
150
|
+
*/
|
|
151
|
+
export const LEGACY_WITH_NEW_SMALL_MIGRATION_EFFORT = [
|
|
152
|
+
{ component: 'DSModal', oldPackage: '@elliemae/ds-modal',
|
|
153
|
+
newComponent: 'DSDialog', newPackage: '@elliemae/ds-dialog',
|
|
154
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
{ component: 'DSButton', oldPackage: '@elliemae/ds-button',
|
|
158
|
+
newComponent: 'DSButtonV3', newPackage: '@elliemae/ds-button',
|
|
159
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
{ component: 'DSCircularProgressIndicator', oldPackage: '@elliemae/ds-circular-progress-indicator',
|
|
163
|
+
newComponent: 'DSCircularIndeterminateIndicator', newPackage: '@elliemae/ds-circular-progress-indicator',
|
|
164
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
{ component: 'DSButtonV2', oldPackage: '@elliemae/ds-button',
|
|
168
|
+
newComponent: 'DSButtonV3', newPackage: '@elliemae/ds-button',
|
|
169
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
{ component: 'DSTooltipV2', oldPackage: '@elliemae/ds-tooltip',
|
|
173
|
+
newComponent: 'DSTooltipV3', newPackage: '@elliemae/ds-tooltip',
|
|
174
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
{ component: 'DSSeparator', oldPackage: '@elliemae/ds-separator',
|
|
178
|
+
newComponent: 'DSSeparatorV2', newPackage: '@elliemae/ds-separator',
|
|
179
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
{ component: 'DSFormItemLayout', oldPackage: '@elliemae/ds-form',
|
|
183
|
+
newComponent: 'DSFormLayoutBlockItem', newPackage: '@elliemae/ds-form-layout-blocks',
|
|
184
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
{ component: 'DSTooltip', oldPackage: '@elliemae/ds-tooltip',
|
|
188
|
+
newComponent: 'DSTooltipV3', newPackage: '@elliemae/ds-tooltip',
|
|
189
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
{ component: 'DSToggle', oldPackage: '@elliemae/ds-toggle',
|
|
193
|
+
newComponent: 'DSControlledToggle', newPackage: '@elliemae/ds-controlled-form',
|
|
194
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
{ component: 'DSDateInput', oldPackage: '@elliemae/ds-form',
|
|
198
|
+
newComponent: 'DSControlledDateTimePicker', newPackage: '@elliemae/ds-controlled-form',
|
|
199
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
{ component: 'DSDateInputV2', oldPackage: '@elliemae/ds-form',
|
|
203
|
+
newComponent: 'DSControlledDateTimePicker', newPackage: '@elliemae/ds-controlled-form',
|
|
204
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
{ component: 'DSComboBox2', oldPackage: '@elliemae/ds-form',
|
|
208
|
+
newComponent: 'DSComboBoxV3', newPackage: '@elliemae/ds-controlled-form',
|
|
209
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
{ component: 'DSComboBoxV1', oldPackage: '@elliemae/ds-form',
|
|
213
|
+
newComponent: 'DSComboBoxV3', newPackage: '@elliemae/ds-controlled-form',
|
|
214
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
215
|
+
},
|
|
216
|
+
|
|
217
|
+
{ component: 'DSComboBox', oldPackage: '@elliemae/ds-form',
|
|
218
|
+
newComponent: 'DSComboBoxV3', newPackage: '@elliemae/ds-controlled-form',
|
|
219
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
220
|
+
},
|
|
221
|
+
|
|
222
|
+
{ component: 'DSPill', oldPackage: '@elliemae/ds-pills',
|
|
223
|
+
newComponent: 'DSPillV2', newPackage: '@elliemae/ds-pills-v2',
|
|
224
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
{ component: 'DSCheckbox', oldPackage: '@elliemae/ds-form',
|
|
228
|
+
newComponent: 'DSControlledCheckbox', newPackage: '@elliemae/ds-controlled-form',
|
|
229
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
230
|
+
},
|
|
231
|
+
|
|
232
|
+
{ component: 'DSInputGroup', oldPackage: '@elliemae/ds-form',
|
|
233
|
+
newComponent: 'DSInputGroup', newPackage: '@elliemae/ds-controlled-form',
|
|
234
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
{ component: 'DSRadio', oldPackage: '@elliemae/ds-form',
|
|
238
|
+
newComponent: 'DSControlledRadio', newPackage: '@elliemae/ds-controlled-form',
|
|
239
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
{ component: 'DSTextBox', oldPackage: '@elliemae/ds-form',
|
|
243
|
+
newComponent: 'DSInputText', newPackage: '@elliemae/ds-controlled-form',
|
|
244
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
245
|
+
},
|
|
246
|
+
|
|
247
|
+
{ component: 'DSLargeInputText', oldPackage: '@elliemae/ds-form',
|
|
248
|
+
newComponent: 'DSControlledLargeTextInput', newPackage: '@elliemae/ds-controlled-form',
|
|
249
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
250
|
+
},
|
|
251
|
+
];
|
|
252
|
+
/**
|
|
253
|
+
* @type {DeprecatedPackage[]}
|
|
254
|
+
*/
|
|
255
|
+
/* prettier-ignore */
|
|
256
|
+
export const LEGACY_WITH_NEW_MEDIUM_MIGRATION_EFFORT = [
|
|
257
|
+
{ component: 'DSDropdownMenu', oldPackage: '@elliemae/ds-dropdownmenu',
|
|
258
|
+
newComponent: 'DSDropdownMenuV2', newPackage: '@elliemae/ds-dropdown-menu-v2',
|
|
259
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
260
|
+
},
|
|
261
|
+
|
|
262
|
+
{ component: 'Minitoolbar', oldPackage: '@elliemae/ds-mini-toolbar',
|
|
263
|
+
newComponent: 'DSToolbarV2', newPackage: '@elliemae/ds-toolbar-v2',
|
|
264
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
{ component: 'DSToolbar', oldPackage: '@elliemae/ds-toolbar',
|
|
268
|
+
newComponent: 'DSToolbarV2', newPackage: '@elliemae/ds-toolbar',
|
|
269
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
{ component: 'DSDatePicker', oldPackage: '@elliemae/ds-date-picker',
|
|
273
|
+
newComponent: 'DSControlledDateTimePicker', newPackage: '@elliemae/ds-controlled-form',
|
|
274
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
275
|
+
},
|
|
276
|
+
|
|
277
|
+
{ component: 'DSDateRangePicker', oldPackage: '@elliemae/ds-date-range-picker',
|
|
278
|
+
newComponent: 'DSControlledDateRangePicker', newPackage: '@elliemae/ds-controlled-form',
|
|
279
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
280
|
+
},
|
|
281
|
+
|
|
282
|
+
{ component: 'DSTimeInput', oldPackage: '@elliemae/ds-form',
|
|
283
|
+
newComponent: 'DSControlledDateTimePicker', newPackage: '@elliemae/ds-controlled-form',
|
|
284
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
{ component: 'DSTimePicker', oldPackage: '@elliemae/ds-time-picker',
|
|
288
|
+
newComponent: 'DSControlledDateTimePicker', newPackage: '@elliemae/ds-controlled-form',
|
|
289
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
290
|
+
},
|
|
291
|
+
|
|
292
|
+
{ component: 'DSInputMask', oldPackage: '@elliemae/ds-form',
|
|
293
|
+
newComponent: 'DSInputText', newPackage: '@elliemae/ds-controlled-form',
|
|
294
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
295
|
+
},
|
|
296
|
+
]
|
|
297
|
+
|
|
298
|
+
/* prettier-ignore */
|
|
299
|
+
export const LEGACY_WITH_NEW_LARGE_MIGRATION_EFFORT = [
|
|
300
|
+
{ component: 'DSShuttle', oldPackage: '@elliemae/ds-shuttle',
|
|
301
|
+
newComponent: 'DSShuttleV2', newPackage: '@elliemae/ds-shuttle-v2',
|
|
302
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
{ component: 'DataGrid', oldPackage: '@elliemae/ds-data-grid',
|
|
306
|
+
newComponent: 'DataTable', newPackage: '@elliemae/ds-data-table',
|
|
307
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ],
|
|
308
|
+
},
|
|
309
|
+
];
|
|
310
|
+
|
|
311
|
+
// all of those are considered LOW effort
|
|
312
|
+
/**
|
|
313
|
+
* @type {DeprecatedPackageMatch[]}
|
|
314
|
+
*/
|
|
315
|
+
/* prettier-ignore */
|
|
316
|
+
export const DISMISSED_WITH_EXAMPLE = [
|
|
317
|
+
{ component: 'DSButtonGroup', oldPackage: 'ds-button-group' },
|
|
318
|
+
{ component: 'DSCardArray', oldPackage: 'ds-card-array' },
|
|
319
|
+
{ component: 'DSLabelValue', oldPackage: 'ds-label-value' },
|
|
320
|
+
{ component: 'DSDateRangeSelector', oldPackage: 'ds-date-range-selector' },
|
|
321
|
+
{ component: 'DSInputProtected', oldPackage: 'ds-form' },
|
|
322
|
+
{ component: 'DSGroupBox', oldPackage: 'ds-group-box' },
|
|
323
|
+
{ component: 'DSFilterBar', oldPackage: 'ds-filterbar' },
|
|
324
|
+
{ component: 'DSDateTimeRecurrenceSelector', oldPackage: 'ds-date-time-recurrence-picker' },
|
|
325
|
+
{ component: 'DSNumberRangeField', oldPackage: 'ds-number-range-field' },
|
|
326
|
+
{ component: 'DSPageNumber', oldPackage: 'ds-page-number' },
|
|
327
|
+
{ component: 'DSSearchField', oldPackage: 'ds-search-field' },
|
|
328
|
+
];
|
|
329
|
+
/* eslint-enable max-len, prettier/prettier */
|
|
330
|
+
/*********************************************************************************************
|
|
331
|
+
Formatting is back on :)
|
|
332
|
+
**********************************************************************************************/
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Regarding file matching
|
|
2
|
+
|
|
3
|
+
## Support for Gitignore
|
|
4
|
+
|
|
5
|
+
### pre-analysis
|
|
6
|
+
|
|
7
|
+
based on CLI `man(ual)` git ignore : `man 5 gitignore`
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
GITIGNORE(5) Git Manual GITIGNORE(5)
|
|
11
|
+
|
|
12
|
+
NAME
|
|
13
|
+
gitignore - Specifies intentionally untracked files to ignore
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
specifically:
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
DESCRIPTION
|
|
21
|
+
[...]
|
|
22
|
+
Each line in a gitignore file specifies a pattern.
|
|
23
|
+
[...]
|
|
24
|
+
|
|
25
|
+
PATTERN FORMAT
|
|
26
|
+
[...]
|
|
27
|
+
See fnmatch(3) and the FNM_PATHNAME flag for a more detailed description.
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
the algorithm to support `.gitignore` syntaxes for filtering we need requires the following charateristics:
|
|
31
|
+
|
|
32
|
+
- multiple patterns match
|
|
33
|
+
- `fnmatch` patterns support
|
|
34
|
+
|
|
35
|
+
### Gathering the list of files
|
|
36
|
+
|
|
37
|
+
Ideally speaking we want to find a solution that would support the generation of the filepaths list based on the "gitignore" in one go.
|
|
38
|
+
|
|
39
|
+
The standard filesystem API from nodejs 20 is not able to support glob patterns as of today, it's merely able to generate a list of every single file-path recursively.
|
|
40
|
+
|
|
41
|
+
#### How does other do it?
|
|
42
|
+
|
|
43
|
+
##### Glob - how does it generate the file list?
|
|
44
|
+
|
|
45
|
+
based on my understanding, the creator of `Glob` has also developed a dedicated file-system optimization library he uses in the Glob package, which is [path-scurry](https://www.npmjs.com/package/path-scurry#:~:text=For%20resolving,resolution).
|
|
46
|
+
|
|
47
|
+
> For resolving string paths, PathScurry ranges from 5-50 times faster than path.resolve on repeated resolutions, but around 100 to 1000 times slower on the first resolution.
|
|
48
|
+
> [...]
|
|
49
|
+
> With default settings on a folder tree of 100,000 items, consisting of around a 10-to-1 ratio of normal files to directories, PathScurry performs comparably to @nodelib/fs.walk, which is the fastest and most reliable file system walker I could find.
|
|
50
|
+
> As far as I can tell, it's almost impossible to go much faster in a Node.js program, just based on how fast you can push syscalls out to the fs thread pool.
|
|
51
|
+
|
|
52
|
+
I understand that under the hood `Glob` is NOT applying the matching pattern at the "matching" step itself, it's instead generating a list of all the files and later on filtering it down with `regexp` string matching.
|
|
53
|
+
|
|
54
|
+
I also understand that the optimization path-scurry are providing to the file-list generation are not necessarly beneficial to our scenario and are making a trade-off of memory in favory of execution time, which in CI & CD environments may not be desiderable due to the reduced RAM of the environments, we usually prefeer to have the pipeline run 2x longer but requiring 1/2 the RAM resources rather then taking 1/2 the time but reserving 2x the RAM.
|
|
55
|
+
|
|
56
|
+
[from scurry docs](https://www.npmjs.com/package/path-scurry#:~:text=PathScurry%20makes,can)
|
|
57
|
+
|
|
58
|
+
> PathScurry makes heavy use of LRUCache to efficiently cache whatever it can, and Path objects remain in the graph for the lifetime of the walker
|
|
59
|
+
|
|
60
|
+
##### [Glob](https://www.npmjs.com/package/glob#:~:text=testing,characters) - gitignore support
|
|
61
|
+
|
|
62
|
+
based on my current understanding, the node package `glob` is not specifically about supporting all the possible syntaxes that are meant to be supported by the gitignore patterns:
|
|
63
|
+
|
|
64
|
+
> testing, fast-glob is around 10-20% faster than this module when walking over 200k files nested 4 directories deep 1.
|
|
65
|
+
> However, there are some inconsistencies with Bash matching behavior that this module does not suffer from
|
|
66
|
+
|
|
67
|
+
and it's instead trying to create a bash compliant list of files paths based on bash patterns.
|
|
68
|
+
|
|
69
|
+
for this reason to ensure 1-1 `Glob` matching would require still a further pattern matching on the generated list and I understand we would not necessarly benefit from the trades-off glob is making under the hood.
|
|
70
|
+
|
|
71
|
+
#### What I will do
|
|
72
|
+
|
|
73
|
+
##### step 1 - generate the file-list via native fs readDir
|
|
74
|
+
|
|
75
|
+
I will use the native node implementation to ensure maximum compatibility and to not encur the risk that the glob abstraction may incurr due to the heavy use of the LRUCache, relying on node's own performance optimization. I think that if any enhancement on the performance/memory is required it would be easier to manage if we don't deal with Glob out-of-the-box optimization and start from the bottom-line
|
|
76
|
+
|
|
77
|
+
##### step 2 - further filtering - gitignore compliant
|
|
78
|
+
|
|
79
|
+
I have found out https://github.com/micromatch/micromatch
|
|
80
|
+
|
|
81
|
+
which seems to be used by `square`, `webpack`, `babel core`, `yarn`, `jest`, `taro`, `bulma`, `browser-sync`, `documentation.js`, `stylelint`, `nyc`, `ava`
|
|
82
|
+
|
|
83
|
+
and seems to be matching our requirements.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// -----------------------------------------------------------------------------
|
|
2
|
+
// Debug helpers for ram issues
|
|
3
|
+
// -----------------------------------------------------------------------------
|
|
4
|
+
/**
|
|
5
|
+
* a helper function to format memory usage data into a human readable format
|
|
6
|
+
* @param {number} data - memory usage data
|
|
7
|
+
* @returns {string} - formatted memory usage data in MB
|
|
8
|
+
*/
|
|
9
|
+
const formatMemoryUsage = (data) => `${Math.round((data / 1024 / 1024) * 100) / 100} MB`;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* logs the current memory usage
|
|
13
|
+
* @param {object} memoryData - memory usage data - process.memoryUsage()
|
|
14
|
+
* @param {string} [prefix=''] - prefix for the log
|
|
15
|
+
* @returns {void}
|
|
16
|
+
*/
|
|
17
|
+
export const logCurrMemoryUsage = (memoryData, prefix = '') => {
|
|
18
|
+
const memoryUsage = {
|
|
19
|
+
// 'process total allocated memory': `${formatMemoryUsage(memoryData.rss)}`, // not relevant?
|
|
20
|
+
'heap total size': `${formatMemoryUsage(memoryData.heapTotal)}`,
|
|
21
|
+
'current heap memory used': `${formatMemoryUsage(memoryData.heapUsed)}`,
|
|
22
|
+
// 'v8 external memory': `${formatMemoryUsage(memoryData.external)}`, // not relevant?
|
|
23
|
+
};
|
|
24
|
+
console.log('************************************************************************');
|
|
25
|
+
console.log(prefix, memoryUsage);
|
|
26
|
+
console.log('************************************************************************');
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* logs the difference between two memory usage data objects
|
|
31
|
+
* @param {object} memoryDataBefore - memory usage data - process.memoryUsage()
|
|
32
|
+
* @param {object} memoryDataAfter - memory usage data - process.memoryUsage()
|
|
33
|
+
* @param {string} [prefix=''] - prefix for the log
|
|
34
|
+
* @returns {void}
|
|
35
|
+
*/
|
|
36
|
+
export const logMemoryUsageDiff = (memoryDataBefore, memoryDataAfter, prefix = '') => {
|
|
37
|
+
// const diffRSS = memoryDataAfter.rss - memoryDataBefore.rss;
|
|
38
|
+
const diffHeapTotal = memoryDataAfter.heapTotal - memoryDataBefore.heapTotal;
|
|
39
|
+
const diffHeapUsed = memoryDataAfter.heapUsed - memoryDataBefore.heapUsed;
|
|
40
|
+
// const diffExternal = memoryDataAfter.external - memoryDataBefore.external;
|
|
41
|
+
const diffData = {
|
|
42
|
+
// 'process total allocated memory': `${formatMemoryUsage(memoryDataBefore.rss)} ${
|
|
43
|
+
// diffRSS > 0 ? '+' : ''
|
|
44
|
+
// }${formatMemoryUsage(diffRSS)}`, // not relevant?
|
|
45
|
+
'heap total size': `${formatMemoryUsage(memoryDataBefore.heapTotal)} ${
|
|
46
|
+
diffHeapTotal > 0 ? '+' : ''
|
|
47
|
+
}${formatMemoryUsage(diffHeapTotal)}`,
|
|
48
|
+
'current heap memory used': `${formatMemoryUsage(memoryDataBefore.heapUsed)} ${
|
|
49
|
+
diffHeapUsed > 0 ? '+' : ''
|
|
50
|
+
}${formatMemoryUsage(diffHeapUsed)}`,
|
|
51
|
+
// 'v8 external memory': `${formatMemoryUsage(memoryDataBefore.external)} ${
|
|
52
|
+
// diffExternal > 0 ? '+' : ''
|
|
53
|
+
// }${formatMemoryUsage(diffExternal)}`, // not relevant?
|
|
54
|
+
};
|
|
55
|
+
console.log('************************************************************************');
|
|
56
|
+
console.log(prefix, diffData);
|
|
57
|
+
console.log('************************************************************************');
|
|
58
|
+
};
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/* eslint-disable max-lines */
|
|
2
|
+
/* eslint-disable indent */
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { readdirSync, readFileSync } from 'fs';
|
|
5
|
+
import { logCurrMemoryUsage, logMemoryUsageDiff } from './debugHelpers.mjs';
|
|
6
|
+
import micromatch from 'micromatch'; // used to filter out files that match the git ignore patterns
|
|
7
|
+
|
|
8
|
+
/********************************************************************
|
|
9
|
+
* ******* GENERIC STRING HELPERS COMMON TO PATHS & FILENAMES *******
|
|
10
|
+
********************************************************************/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* convert windows paths to POSIX (glob compatible) slashes ( \ -- to --> /).
|
|
14
|
+
*
|
|
15
|
+
* @param {string} pathString the path to convert
|
|
16
|
+
* @returns {string} path with glob compatible POSIX slashes ( \ -- to --> /).
|
|
17
|
+
*/
|
|
18
|
+
const generatePosixPath = (pathString) => pathString.replace(/\\/g, '/');
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* prepend process.cwd() to a string and convert to POSIX (glob compatible) slashes ( \ -- to --> /).
|
|
22
|
+
*
|
|
23
|
+
* @param {string} stringToPrependTo the string to prepend the CWD and make glob compatible
|
|
24
|
+
* @returns {string} paths with prepent CWD and glob compatible POSIX slashes ( \ -- to --> /).
|
|
25
|
+
*/
|
|
26
|
+
export const generatePathFromCurrentFolder = (...stringToPrependTo) =>
|
|
27
|
+
generatePosixPath(path.join(process.cwd(), ...stringToPrependTo));
|
|
28
|
+
|
|
29
|
+
/********************************************************************
|
|
30
|
+
*** DIRECTORY RECURSIONS HELPERS - NO IGNORING DURING ITERATION ****
|
|
31
|
+
********************************************************************/
|
|
32
|
+
/**
|
|
33
|
+
* generates a list of files paths from a starting path iterating dirs recursively -
|
|
34
|
+
* in node 20 this is potentially really slow per se,
|
|
35
|
+
* we are implementing a micro-optimization by implementing it internally.
|
|
36
|
+
*
|
|
37
|
+
* This should not be required if
|
|
38
|
+
* - node 20 fixes the performance issue with `readdirSync` -> recursive: true.
|
|
39
|
+
* - we only need to support node 20+.
|
|
40
|
+
*
|
|
41
|
+
* withFileTypes: true - to avoid the extra `stat` system call.
|
|
42
|
+
*
|
|
43
|
+
* This function is also converting the paths to POSIX (glob compatible) internally, no need for windows specific code.
|
|
44
|
+
* @param {string} startingPath - path to start generating the list of files from
|
|
45
|
+
* @returns {string[]} - list of files paths
|
|
46
|
+
* @see https://nodejs.org/api/fs.html#fsreaddirsyncpath-options
|
|
47
|
+
* @see https://stackoverflow.com/a/76632197/1689370
|
|
48
|
+
*/
|
|
49
|
+
const getFilesMicroOptimized = (dir) => {
|
|
50
|
+
let dirsToProcess = [dir];
|
|
51
|
+
const outputFiles = [];
|
|
52
|
+
while (dirsToProcess.length > 0) {
|
|
53
|
+
const dir = dirsToProcess.pop();
|
|
54
|
+
|
|
55
|
+
const fileList = readdirSync(dir, { withFileTypes: true });
|
|
56
|
+
fileList.forEach((file) => {
|
|
57
|
+
const filePath = generatePosixPath(`${dir}/${file.name}`);
|
|
58
|
+
if (file.isDirectory()) {
|
|
59
|
+
dirsToProcess.push(filePath);
|
|
60
|
+
} else {
|
|
61
|
+
outputFiles.push(filePath);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return outputFiles;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* generates a list of files paths from a starting path recursively -
|
|
70
|
+
* in node 20 this is potentially really slow per se, we are implementing a micro-optimization:
|
|
71
|
+
* we invoke the function recursively only on directories as a replacement for the `recursive` option of `readdirSync`
|
|
72
|
+
* and additionally we use `withFileTypes` option to avoid the extra `stat` system call.
|
|
73
|
+
*
|
|
74
|
+
* the way this is implemented we can use it on node < 20 as well.
|
|
75
|
+
*
|
|
76
|
+
* This function is also converting the paths to POSIX (glob compatible) internally, no need for windows specific code.
|
|
77
|
+
* @param {string} startingPath - path to start generating the list of files from
|
|
78
|
+
* @returns {string[]} - list of files paths
|
|
79
|
+
* @see https://nodejs.org/api/fs.html#fsreaddirsyncpath-options
|
|
80
|
+
* @see https://stackoverflow.com/a/76632197/1689370
|
|
81
|
+
*/
|
|
82
|
+
export const fullFilesPaths = (startingPath, debug = false) => {
|
|
83
|
+
let startMemoryUsage;
|
|
84
|
+
let endMemoryUsage;
|
|
85
|
+
let startTime;
|
|
86
|
+
if (debug) {
|
|
87
|
+
startTime = performance.now();
|
|
88
|
+
startMemoryUsage = process.memoryUsage();
|
|
89
|
+
logCurrMemoryUsage(startMemoryUsage, 'before recursion start');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// this is split into an helper function to make it explicit that
|
|
93
|
+
// we know about the `recursive` option of `readdirSync` and we are not using it on purpose.
|
|
94
|
+
// required if running on node < 20
|
|
95
|
+
// because of the lack of the `recursive` option of `readdirSync` in those versions.
|
|
96
|
+
const files = getFilesMicroOptimized(generatePathFromCurrentFolder(startingPath));
|
|
97
|
+
|
|
98
|
+
if (debug) {
|
|
99
|
+
endMemoryUsage = process.memoryUsage();
|
|
100
|
+
logMemoryUsageDiff(startMemoryUsage, endMemoryUsage, 'diff after recursion end');
|
|
101
|
+
// eslint-disable-next-line no-console
|
|
102
|
+
console.log('Estimated execution time: ', performance.now() - startTime);
|
|
103
|
+
// eslint-disable-next-line no-console
|
|
104
|
+
console.log('List of file entries generated:', files.length);
|
|
105
|
+
}
|
|
106
|
+
return files;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/********************************************************************
|
|
110
|
+
* DIRECTORY RECURSIONS HELPERS - IGNORING PATTERNS WHILE ITERATING *
|
|
111
|
+
this is not used, but keeping it around for reference,
|
|
112
|
+
as of 04/december/2023 this is slower than filtering at the end.
|
|
113
|
+
********************************************************************/
|
|
114
|
+
|
|
115
|
+
// Required if running on node < 20 becuase of the lack of the `recursive` option of `readdirSync` in those versions.
|
|
116
|
+
// const getFilesMicroOptimizedWithIgnore = (startingPath, gitPatterns) => {
|
|
117
|
+
// let dirsToProcess = [generatePathFromCurrentFolder(startingPath)];
|
|
118
|
+
// const outputFiles = [];
|
|
119
|
+
// // the iterative approach avoids stack overflow errors
|
|
120
|
+
// // allowing us to invoke the filtering function while iterating the directories
|
|
121
|
+
// while (dirsToProcess.length > 0) {
|
|
122
|
+
// const dir = dirsToProcess.pop();
|
|
123
|
+
// // we filter out the ignored files and directories here
|
|
124
|
+
// const fileList = readdirSync(dir, { withFileTypes: true }).filter(
|
|
125
|
+
// (file) => !micromatch.isMatch(generatePosixPath(`${dir}/${file.name}`), gitPatterns, { contains: true }),
|
|
126
|
+
// );
|
|
127
|
+
// fileList.forEach((file) => {
|
|
128
|
+
// const filePath = generatePosixPath(`${dir}/${file.name}`);
|
|
129
|
+
// if (file.isDirectory()) {
|
|
130
|
+
// dirsToProcess.push(filePath);
|
|
131
|
+
// } else {
|
|
132
|
+
// outputFiles.push(filePath);
|
|
133
|
+
// }
|
|
134
|
+
// });
|
|
135
|
+
// }
|
|
136
|
+
// return outputFiles;
|
|
137
|
+
// };
|
|
138
|
+
// const fullFilesPathsWithIgnore = (startingPath, gitPatterns, debug = false) => {
|
|
139
|
+
// let startMemoryUsage;
|
|
140
|
+
// let endMemoryUsage;
|
|
141
|
+
// let startTime;
|
|
142
|
+
// if (debug) {
|
|
143
|
+
// startTime = performance.now();
|
|
144
|
+
// startMemoryUsage = process.memoryUsage();
|
|
145
|
+
// logCurrMemoryUsage(startMemoryUsage, 'before recursion start');
|
|
146
|
+
// }
|
|
147
|
+
// // this is split into an helper function to make it explicit that
|
|
148
|
+
// // we know about the `recursive` option of `readdirSync` and we are not using it on purpose.
|
|
149
|
+
// const files = getFilesMicroOptimizedWithIgnore(startingPath, gitPatterns, {
|
|
150
|
+
// debug,
|
|
151
|
+
// startMemoryUsage,
|
|
152
|
+
// });
|
|
153
|
+
|
|
154
|
+
// if (debug) {
|
|
155
|
+
// endMemoryUsage = process.memoryUsage();
|
|
156
|
+
// logMemoryUsageDiff(startMemoryUsage, endMemoryUsage, 'diff after recursion end');
|
|
157
|
+
// console.log('Estimated execution time: ', performance.now() - startTime);
|
|
158
|
+
// console.log('List of file entries generated:', files.length);
|
|
159
|
+
// }
|
|
160
|
+
// return files;
|
|
161
|
+
// };
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* generates a list of files paths from a starting path recursively,
|
|
165
|
+
* filtering out files that match the git ignore patterns
|
|
166
|
+
* @param {object} options - options
|
|
167
|
+
* @param {string} options.startingDirPath - path to start generating the list of files from
|
|
168
|
+
* @param {string} options.gitIgnorePath - path to the .gitignore file
|
|
169
|
+
* @param {boolean} options.debug - debug flag
|
|
170
|
+
* @param {string} options.fileExtensions - comma separated list of file extensions to filter by
|
|
171
|
+
* @returns {string[]} - list of files paths
|
|
172
|
+
*
|
|
173
|
+
**/
|
|
174
|
+
export const filePathsWithGitIgnore = (options) => {
|
|
175
|
+
const { startingDirPath, gitIgnorePath, debug = false, fileExtensions = '.js,.jsx,.ts,.tsx,.mjs' } = options;
|
|
176
|
+
// we generate a list of patterns to ignore from the .gitignore file.
|
|
177
|
+
const gitIgnoreFileContent = readFileSync(generatePathFromCurrentFolder(gitIgnorePath), 'utf8');
|
|
178
|
+
const gitIgnoreFileContentLines = gitIgnoreFileContent.split(/\r?\n/);
|
|
179
|
+
// remove empty and commented lines
|
|
180
|
+
const gitPatterns = gitIgnoreFileContentLines.filter((line) => line !== '' && !line.startsWith('#'));
|
|
181
|
+
|
|
182
|
+
// console.log('--------------- ----------------- filtering as we go:');
|
|
183
|
+
// const files = fullFilesPathsWithIgnore(startingDirPath, gitPatterns, debug);
|
|
184
|
+
// console.log(files.length);
|
|
185
|
+
|
|
186
|
+
// Based on my testing, filtering at the end results in way better performance than filtering as we go...
|
|
187
|
+
// console.log('--------------- ----------------- filtering at the end:');
|
|
188
|
+
const nonGitIgnoredFiles = fullFilesPaths(startingDirPath, debug).filter(
|
|
189
|
+
(file) => !micromatch.isMatch(file, gitPatterns, { contains: true }),
|
|
190
|
+
);
|
|
191
|
+
// we only want files matching the file extensions
|
|
192
|
+
const files = nonGitIgnoredFiles.filter((filePath) => {
|
|
193
|
+
const dotlessExtension = filePath.split('.').pop();
|
|
194
|
+
return fileExtensions.split(',').some((fileExtension) => fileExtension === `.${dotlessExtension}`);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
return files;
|
|
198
|
+
};
|
package/bin/cli/utils/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export
|
|
5
|
-
export
|
|
1
|
+
export { generatePathFromCurrentFolder, fullFilesPaths } from './filepathsMatchers.mjs';
|
|
2
|
+
export { globArray } from './globArray.mjs';
|
|
3
|
+
export { replaceFromMap } from './replaceFromMap.mjs';
|
|
4
|
+
export { getVersions, getMatchVersions, getHighestMatchVersion } from './matchHelpers.mjs';
|
|
5
|
+
export { getLatestDimsumVersion } from './getLatestDimsumVersion.mjs';
|
package/package.json
CHANGED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
export const SEVERITY = {
|
|
2
|
-
NON_STARTER: ['Failing to upgrade will make the project impossible to start/use', 1],
|
|
3
|
-
KNOWN_ISSUES: [
|
|
4
|
-
'The component has unfixable known issues. Failing to upgrade means APP will have to live with those',
|
|
5
|
-
2,
|
|
6
|
-
],
|
|
7
|
-
FRAGILE_AND_UNMANTAINED: [
|
|
8
|
-
'Failing to upgrade will make the project fragile and any error will have to be addressed by the APP team',
|
|
9
|
-
3,
|
|
10
|
-
],
|
|
11
|
-
UNMANTAINED: ['Failing to upgrade will mean that any error will have to be addressed by the APP team', 4],
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export const SOLUTIONS = {
|
|
15
|
-
REMOVED: 'the component has been regarded as supreflous and has hencheforth been removed in favor of examples',
|
|
16
|
-
NEW_VERSION_BREAKING: 'the component impedes further enhancments/bugfixes, a new version ex-novo has been created.',
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export const DEPRECATED_PACKAGES = {
|
|
20
|
-
'@elliemae/ds-datagrids': {
|
|
21
|
-
name: 'data-grid',
|
|
22
|
-
solution: SOLUTIONS.NEW_VERSION_BREAKING,
|
|
23
|
-
severity: SEVERITY.KNOWN_ISSUES,
|
|
24
|
-
// TODO create relative confluence
|
|
25
|
-
confluence: 'https://confluence.elliemae.io/display/FEAE/Breaking+changes+-+Migration+steps+and+rationale',
|
|
26
|
-
},
|
|
27
|
-
'@elliemae/ds-date-picker': {
|
|
28
|
-
name: 'date-picker',
|
|
29
|
-
solution: SOLUTIONS.NEW_VERSION_BREAKING,
|
|
30
|
-
severity: SEVERITY.KNOWN_ISSUES,
|
|
31
|
-
// TODO create relative confluence
|
|
32
|
-
confluence: 'https://confluence.elliemae.io/display/FEAE/Breaking+changes+-+Migration+steps+and+rationale',
|
|
33
|
-
},
|
|
34
|
-
'@elliemae/ds-date-range-picker': {
|
|
35
|
-
name: 'date-range-picker',
|
|
36
|
-
solution: SOLUTIONS.NEW_VERSION_BREAKING,
|
|
37
|
-
severity: SEVERITY.KNOWN_ISSUES,
|
|
38
|
-
// TODO create relative confluence
|
|
39
|
-
confluence: 'https://confluence.elliemae.io/display/FEAE/Breaking+changes+-+Migration+steps+and+rationale',
|
|
40
|
-
},
|
|
41
|
-
'@elliemae/ds-date-time-picker': {
|
|
42
|
-
name: 'date-time-picker',
|
|
43
|
-
solution: SOLUTIONS.NEW_VERSION_BREAKING,
|
|
44
|
-
severity: SEVERITY.KNOWN_ISSUES,
|
|
45
|
-
// TODO create relative confluence
|
|
46
|
-
confluence: 'https://confluence.elliemae.io/display/FEAE/Breaking+changes+-+Migration+steps+and+rationale',
|
|
47
|
-
},
|
|
48
|
-
'@elliemae/ds-time-picker': {
|
|
49
|
-
name: 'time-picker',
|
|
50
|
-
solution: SOLUTIONS.NEW_VERSION_BREAKING,
|
|
51
|
-
severity: SEVERITY.KNOWN_ISSUES,
|
|
52
|
-
// TODO create relative confluence
|
|
53
|
-
confluence: 'https://confluence.elliemae.io/display/FEAE/Breaking+changes+-+Migration+steps+and+rationale',
|
|
54
|
-
},
|
|
55
|
-
'@elliemae/ds-button-group': {
|
|
56
|
-
name: 'button-group',
|
|
57
|
-
solution: SOLUTIONS.REMOVED,
|
|
58
|
-
severity: SEVERITY.NON_STARTER,
|
|
59
|
-
confluence: 'https://confluence.elliemae.io/display/FEAE/Button+Group+-+Dimsum+2.x',
|
|
60
|
-
},
|
|
61
|
-
'@elliemae/ds-card-array': {
|
|
62
|
-
name: 'card-array',
|
|
63
|
-
solution: SOLUTIONS.REMOVED,
|
|
64
|
-
severity: SEVERITY.NON_STARTER,
|
|
65
|
-
confluence: 'https://confluence.elliemae.io/display/FEAE/Card+Array+-+Dimsum+2.x',
|
|
66
|
-
},
|
|
67
|
-
'@elliemae/ds-form': {
|
|
68
|
-
subComponents: [
|
|
69
|
-
{
|
|
70
|
-
name: 'combobox',
|
|
71
|
-
solution: SOLUTIONS.NEW_VERSION_BREAKING,
|
|
72
|
-
severity: SEVERITY.FRAGILE_AND_UNMANTAINED,
|
|
73
|
-
confluence: 'https://confluence.elliemae.io/display/FEAE/Combo-box+-+Dimsum+2.x',
|
|
74
|
-
},
|
|
75
|
-
],
|
|
76
|
-
},
|
|
77
|
-
'@elliemae/ds-combobox-v1': {
|
|
78
|
-
subComponents: [
|
|
79
|
-
{
|
|
80
|
-
name: 'combobox',
|
|
81
|
-
solution: SOLUTIONS.NEW_VERSION_BREAKING,
|
|
82
|
-
severity: SEVERITY.FRAGILE_AND_UNMANTAINED,
|
|
83
|
-
confluence: 'https://confluence.elliemae.io/display/FEAE/Combo-box+-+Dimsum+2.x',
|
|
84
|
-
},
|
|
85
|
-
],
|
|
86
|
-
},
|
|
87
|
-
};
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
/**
|
|
3
|
-
* prepend process.cwd() to a string and convert to POSIX (glob compatible) slashes ( \ -- to --> /).
|
|
4
|
-
*
|
|
5
|
-
* @param {string} stringToPrependTo the string to prepend the CWD and make glob compatible
|
|
6
|
-
* @returns {string} paths with prepent CWD and glob compatible POSIX slashes ( \ -- to --> /).
|
|
7
|
-
*/
|
|
8
|
-
export const generatePathFromCurrentFolder = (...stringToPrependTo) =>
|
|
9
|
-
path.join(process.cwd(), ...stringToPrependTo).replace(/\\/g, '/');
|
|
10
|
-
|
|
11
|
-
export default generatePathFromCurrentFolder;
|