eslint 8.21.0 → 8.23.1
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/eslint.js +2 -4
- package/conf/globals.js +6 -1
- package/lib/cli-engine/file-enumerator.js +4 -2
- package/lib/cli.js +123 -29
- package/lib/config/flat-config-array.js +53 -12
- package/lib/eslint/eslint-helpers.js +39 -16
- package/lib/eslint/flat-eslint.js +29 -17
- package/lib/linter/code-path-analysis/code-path-segment.js +2 -2
- package/lib/linter/code-path-analysis/code-path-state.js +7 -7
- package/lib/linter/code-path-analysis/debug-helpers.js +3 -3
- package/lib/linter/code-path-analysis/id-generator.js +2 -2
- package/lib/linter/config-comment-parser.js +1 -2
- package/lib/linter/timing.js +4 -4
- package/lib/options.js +290 -242
- package/lib/rule-tester/flat-rule-tester.js +1 -1
- package/lib/rule-tester/rule-tester.js +1 -1
- package/lib/rules/array-callback-return.js +1 -1
- package/lib/rules/global-require.js +2 -1
- package/lib/rules/indent-legacy.js +4 -4
- package/lib/rules/indent.js +23 -15
- package/lib/rules/new-cap.js +2 -2
- package/lib/rules/no-extra-boolean-cast.js +1 -1
- package/lib/rules/no-extra-parens.js +2 -2
- package/lib/rules/no-fallthrough.js +8 -3
- package/lib/rules/no-labels.js +1 -1
- package/lib/rules/no-lone-blocks.js +1 -1
- package/lib/rules/no-useless-computed-key.js +1 -1
- package/lib/rules/no-var.js +1 -1
- package/lib/rules/no-warning-comments.js +24 -5
- package/lib/rules/object-shorthand.js +15 -0
- package/lib/rules/padded-blocks.js +1 -1
- package/lib/rules/prefer-arrow-callback.js +2 -2
- package/lib/rules/prefer-const.js +13 -1
- package/lib/rules/prefer-rest-params.js +1 -1
- package/lib/rules/require-yield.js +0 -1
- package/lib/rules/utils/ast-utils.js +10 -4
- package/lib/shared/logging.js +1 -1
- package/lib/shared/types.js +1 -1
- package/lib/source-code/token-store/cursor.js +1 -1
- package/package.json +9 -8
package/lib/rules/indent.js
CHANGED
@@ -12,7 +12,7 @@
|
|
12
12
|
// Requirements
|
13
13
|
//------------------------------------------------------------------------------
|
14
14
|
|
15
|
-
const
|
15
|
+
const { OrderedMap } = require("js-sdsl");
|
16
16
|
|
17
17
|
const astUtils = require("./utils/ast-utils");
|
18
18
|
|
@@ -135,7 +135,8 @@ class BinarySearchTree {
|
|
135
135
|
* Creates an empty tree
|
136
136
|
*/
|
137
137
|
constructor() {
|
138
|
-
this.
|
138
|
+
this._orderedMap = new OrderedMap();
|
139
|
+
this._orderedMapEnd = this._orderedMap.end();
|
139
140
|
}
|
140
141
|
|
141
142
|
/**
|
@@ -145,13 +146,7 @@ class BinarySearchTree {
|
|
145
146
|
* @returns {void}
|
146
147
|
*/
|
147
148
|
insert(key, value) {
|
148
|
-
|
149
|
-
|
150
|
-
if (iterator.valid) {
|
151
|
-
this._rbTree = iterator.update(value);
|
152
|
-
} else {
|
153
|
-
this._rbTree = this._rbTree.insert(key, value);
|
154
|
-
}
|
149
|
+
this._orderedMap.setElement(key, value);
|
155
150
|
}
|
156
151
|
|
157
152
|
/**
|
@@ -160,9 +155,13 @@ class BinarySearchTree {
|
|
160
155
|
* @returns {{key: number, value: *}|null} The found entry, or null if no such entry exists.
|
161
156
|
*/
|
162
157
|
findLe(key) {
|
163
|
-
const iterator = this.
|
158
|
+
const iterator = this._orderedMap.reverseLowerBound(key);
|
164
159
|
|
165
|
-
|
160
|
+
if (iterator.equals(this._orderedMapEnd)) {
|
161
|
+
return {};
|
162
|
+
}
|
163
|
+
|
164
|
+
return { key: iterator.pointer[0], value: iterator.pointer[1] };
|
166
165
|
}
|
167
166
|
|
168
167
|
/**
|
@@ -177,11 +176,20 @@ class BinarySearchTree {
|
|
177
176
|
if (start === end) {
|
178
177
|
return;
|
179
178
|
}
|
180
|
-
const iterator = this.
|
179
|
+
const iterator = this._orderedMap.lowerBound(start);
|
181
180
|
|
182
|
-
|
183
|
-
|
184
|
-
|
181
|
+
if (iterator.equals(this._orderedMapEnd)) {
|
182
|
+
return;
|
183
|
+
}
|
184
|
+
|
185
|
+
if (end > this._orderedMap.back()[0]) {
|
186
|
+
while (!iterator.equals(this._orderedMapEnd)) {
|
187
|
+
this._orderedMap.eraseElementByIterator(iterator);
|
188
|
+
}
|
189
|
+
} else {
|
190
|
+
while (iterator.pointer[0] < end) {
|
191
|
+
this._orderedMap.eraseElementByIterator(iterator);
|
192
|
+
}
|
185
193
|
}
|
186
194
|
}
|
187
195
|
}
|
package/lib/rules/new-cap.js
CHANGED
@@ -39,10 +39,10 @@ const CAPS_ALLOWED = [
|
|
39
39
|
*/
|
40
40
|
function checkArray(obj, key, fallback) {
|
41
41
|
|
42
|
-
/*
|
42
|
+
/* c8 ignore start */
|
43
43
|
if (Object.prototype.hasOwnProperty.call(obj, key) && !Array.isArray(obj[key])) {
|
44
44
|
throw new TypeError(`${key}, if provided, must be an Array`);
|
45
|
-
}
|
45
|
+
}/* c8 ignore stop */
|
46
46
|
return obj[key] || fallback;
|
47
47
|
}
|
48
48
|
|
@@ -634,10 +634,10 @@ module.exports = {
|
|
634
634
|
|
635
635
|
currentNode = currentNode.parent;
|
636
636
|
|
637
|
-
/*
|
637
|
+
/* c8 ignore start */
|
638
638
|
if (currentNode === null) {
|
639
639
|
throw new Error("Nodes are not in the ancestor-descendant relationship.");
|
640
|
-
}
|
640
|
+
}/* c8 ignore stop */
|
641
641
|
|
642
642
|
path.push(currentNode);
|
643
643
|
}
|
@@ -76,6 +76,10 @@ module.exports = {
|
|
76
76
|
commentPattern: {
|
77
77
|
type: "string",
|
78
78
|
default: ""
|
79
|
+
},
|
80
|
+
allowEmptyCase: {
|
81
|
+
type: "boolean",
|
82
|
+
default: false
|
79
83
|
}
|
80
84
|
},
|
81
85
|
additionalProperties: false
|
@@ -91,6 +95,7 @@ module.exports = {
|
|
91
95
|
const options = context.options[0] || {};
|
92
96
|
let currentCodePath = null;
|
93
97
|
const sourceCode = context.getSourceCode();
|
98
|
+
const allowEmptyCase = options.allowEmptyCase || false;
|
94
99
|
|
95
100
|
/*
|
96
101
|
* We need to use leading comments of the next SwitchCase node because
|
@@ -104,7 +109,6 @@ module.exports = {
|
|
104
109
|
} else {
|
105
110
|
fallthroughCommentPattern = DEFAULT_FALLTHROUGH_COMMENT;
|
106
111
|
}
|
107
|
-
|
108
112
|
return {
|
109
113
|
onCodePathStart(codePath) {
|
110
114
|
currentCodePath = codePath;
|
@@ -119,7 +123,8 @@ module.exports = {
|
|
119
123
|
* Checks whether or not there is a fallthrough comment.
|
120
124
|
* And reports the previous fallthrough node if that does not exist.
|
121
125
|
*/
|
122
|
-
|
126
|
+
|
127
|
+
if (fallthroughCase && (!hasFallthroughComment(fallthroughCase, node, context, fallthroughCommentPattern))) {
|
123
128
|
context.report({
|
124
129
|
messageId: node.test ? "case" : "default",
|
125
130
|
node
|
@@ -137,7 +142,7 @@ module.exports = {
|
|
137
142
|
* And allows empty cases and the last case.
|
138
143
|
*/
|
139
144
|
if (currentCodePath.currentSegments.some(isReachable) &&
|
140
|
-
(node.consequent.length > 0 || hasBlankLinesBetween(node, nextToken)) &&
|
145
|
+
(node.consequent.length > 0 || (!allowEmptyCase && hasBlankLinesBetween(node, nextToken))) &&
|
141
146
|
node.parent.cases[node.parent.cases.length - 1] !== node) {
|
142
147
|
fallthroughCase = node;
|
143
148
|
}
|
package/lib/rules/no-labels.js
CHANGED
@@ -91,7 +91,7 @@ module.exports = {
|
|
91
91
|
};
|
92
92
|
|
93
93
|
// ES6: report blocks without block-level bindings, or that's only child of another block
|
94
|
-
if (context.
|
94
|
+
if (context.languageOptions.ecmaVersion >= 2015) {
|
95
95
|
ruleDef = {
|
96
96
|
BlockStatement(node) {
|
97
97
|
if (isLoneBlock(node)) {
|
package/lib/rules/no-var.js
CHANGED
@@ -37,6 +37,15 @@ module.exports = {
|
|
37
37
|
},
|
38
38
|
location: {
|
39
39
|
enum: ["start", "anywhere"]
|
40
|
+
},
|
41
|
+
decoration: {
|
42
|
+
type: "array",
|
43
|
+
items: {
|
44
|
+
type: "string",
|
45
|
+
pattern: "^\\S$"
|
46
|
+
},
|
47
|
+
minItems: 1,
|
48
|
+
uniqueItems: true
|
40
49
|
}
|
41
50
|
},
|
42
51
|
additionalProperties: false
|
@@ -53,6 +62,7 @@ module.exports = {
|
|
53
62
|
configuration = context.options[0] || {},
|
54
63
|
warningTerms = configuration.terms || ["todo", "fixme", "xxx"],
|
55
64
|
location = configuration.location || "start",
|
65
|
+
decoration = [...configuration.decoration || []].join(""),
|
56
66
|
selfConfigRegEx = /\bno-warning-comments\b/u;
|
57
67
|
|
58
68
|
/**
|
@@ -64,6 +74,7 @@ module.exports = {
|
|
64
74
|
*/
|
65
75
|
function convertToRegExp(term) {
|
66
76
|
const escaped = escapeRegExp(term);
|
77
|
+
const escapedDecoration = escapeRegExp(decoration);
|
67
78
|
|
68
79
|
/*
|
69
80
|
* When matching at the start, ignore leading whitespace, and
|
@@ -74,18 +85,23 @@ module.exports = {
|
|
74
85
|
* e.g. terms ["TODO"] matches `//TODO something`
|
75
86
|
* $ handles any terms at the end of a comment
|
76
87
|
* e.g. terms ["TODO"] matches `// something TODO`
|
77
|
-
* \s* handles optional leading spaces (for "start" location only)
|
78
|
-
* e.g. terms ["TODO"] matches `// TODO something`
|
79
88
|
* \b handles terms preceded/followed by word boundary
|
80
89
|
* e.g. terms: ["!FIX", "FIX!"] matches `// FIX!something` or `// something!FIX`
|
81
90
|
* terms: ["FIX"] matches `// FIX!` or `// !FIX`, but not `// fixed or affix`
|
91
|
+
*
|
92
|
+
* For location start:
|
93
|
+
* [\s]* handles optional leading spaces
|
94
|
+
* e.g. terms ["TODO"] matches `// TODO something`
|
95
|
+
* [\s\*]* (where "\*" is the escaped string of decoration)
|
96
|
+
* handles optional leading spaces or decoration characters (for "start" location only)
|
97
|
+
* e.g. terms ["TODO"] matches `/**** TODO something ... `
|
82
98
|
*/
|
83
99
|
const wordBoundary = "\\b";
|
84
100
|
|
85
101
|
let prefix = "";
|
86
102
|
|
87
103
|
if (location === "start") {
|
88
|
-
prefix =
|
104
|
+
prefix = `^[\\s${escapedDecoration}]*`;
|
89
105
|
} else if (/^\w/u.test(term)) {
|
90
106
|
prefix = wordBoundary;
|
91
107
|
}
|
@@ -95,12 +111,15 @@ module.exports = {
|
|
95
111
|
|
96
112
|
/*
|
97
113
|
* For location "start", the typical regex is:
|
98
|
-
*
|
114
|
+
* /^[\s]*ESCAPED_TERM\b/iu.
|
115
|
+
* Or if decoration characters are specified (e.g. "*"), then any of
|
116
|
+
* those characters may appear in any order at the start:
|
117
|
+
* /^[\s\*]*ESCAPED_TERM\b/iu.
|
99
118
|
*
|
100
119
|
* For location "anywhere" the typical regex is
|
101
120
|
* /\bESCAPED_TERM\b/iu
|
102
121
|
*
|
103
|
-
* If it starts or ends with non-word character, the prefix and suffix empty, respectively.
|
122
|
+
* If it starts or ends with non-word character, the prefix and suffix are empty, respectively.
|
104
123
|
*/
|
105
124
|
return new RegExp(`${prefix}${escaped}${suffix}`, flags);
|
106
125
|
}
|
@@ -78,6 +78,9 @@ module.exports = {
|
|
78
78
|
ignoreConstructors: {
|
79
79
|
type: "boolean"
|
80
80
|
},
|
81
|
+
methodsIgnorePattern: {
|
82
|
+
type: "string"
|
83
|
+
},
|
81
84
|
avoidQuotes: {
|
82
85
|
type: "boolean"
|
83
86
|
},
|
@@ -115,6 +118,9 @@ module.exports = {
|
|
115
118
|
|
116
119
|
const PARAMS = context.options[1] || {};
|
117
120
|
const IGNORE_CONSTRUCTORS = PARAMS.ignoreConstructors;
|
121
|
+
const METHODS_IGNORE_PATTERN = PARAMS.methodsIgnorePattern
|
122
|
+
? new RegExp(PARAMS.methodsIgnorePattern, "u")
|
123
|
+
: null;
|
118
124
|
const AVOID_QUOTES = PARAMS.avoidQuotes;
|
119
125
|
const AVOID_EXPLICIT_RETURN_ARROWS = !!PARAMS.avoidExplicitReturnArrows;
|
120
126
|
const sourceCode = context.getSourceCode();
|
@@ -457,6 +463,15 @@ module.exports = {
|
|
457
463
|
if (IGNORE_CONSTRUCTORS && node.key.type === "Identifier" && isConstructor(node.key.name)) {
|
458
464
|
return;
|
459
465
|
}
|
466
|
+
|
467
|
+
if (METHODS_IGNORE_PATTERN) {
|
468
|
+
const propertyName = astUtils.getStaticPropertyName(node);
|
469
|
+
|
470
|
+
if (propertyName !== null && METHODS_IGNORE_PATTERN.test(propertyName)) {
|
471
|
+
return;
|
472
|
+
}
|
473
|
+
}
|
474
|
+
|
460
475
|
if (AVOID_QUOTES && isStringLiteral(node.key)) {
|
461
476
|
return;
|
462
477
|
}
|
@@ -53,7 +53,7 @@ function getVariableOfArguments(scope) {
|
|
53
53
|
}
|
54
54
|
}
|
55
55
|
|
56
|
-
/*
|
56
|
+
/* c8 ignore next */
|
57
57
|
return null;
|
58
58
|
}
|
59
59
|
|
@@ -126,7 +126,7 @@ function getCallbackInfo(node) {
|
|
126
126
|
parent = parent.parent;
|
127
127
|
}
|
128
128
|
|
129
|
-
/*
|
129
|
+
/* c8 ignore next */
|
130
130
|
throw new Error("unreachable");
|
131
131
|
}
|
132
132
|
|
@@ -446,7 +446,19 @@ module.exports = {
|
|
446
446
|
|
447
447
|
reportCount += nodesToReport.length;
|
448
448
|
|
449
|
-
|
449
|
+
let totalDeclarationsCount = 0;
|
450
|
+
|
451
|
+
varDeclParent.declarations.forEach(declaration => {
|
452
|
+
if (declaration.id.type === "ObjectPattern") {
|
453
|
+
totalDeclarationsCount += declaration.id.properties.length;
|
454
|
+
} else if (declaration.id.type === "ArrayPattern") {
|
455
|
+
totalDeclarationsCount += declaration.id.elements.length;
|
456
|
+
} else {
|
457
|
+
totalDeclarationsCount += 1;
|
458
|
+
}
|
459
|
+
});
|
460
|
+
|
461
|
+
shouldFix = shouldFix && (reportCount === totalDeclarationsCount);
|
450
462
|
}
|
451
463
|
}
|
452
464
|
|
@@ -1350,7 +1350,7 @@ module.exports = {
|
|
1350
1350
|
}
|
1351
1351
|
}
|
1352
1352
|
|
1353
|
-
/*
|
1353
|
+
/* c8 ignore next */
|
1354
1354
|
return true;
|
1355
1355
|
},
|
1356
1356
|
|
@@ -1978,7 +1978,7 @@ module.exports = {
|
|
1978
1978
|
if (comments.length) {
|
1979
1979
|
const lastComment = comments[comments.length - 1];
|
1980
1980
|
|
1981
|
-
if (lastComment.range[0] > leftToken.range[0]) {
|
1981
|
+
if (!leftToken || lastComment.range[0] > leftToken.range[0]) {
|
1982
1982
|
leftToken = lastComment;
|
1983
1983
|
}
|
1984
1984
|
}
|
@@ -1986,7 +1986,13 @@ module.exports = {
|
|
1986
1986
|
leftToken = leftValue;
|
1987
1987
|
}
|
1988
1988
|
|
1989
|
-
|
1989
|
+
/*
|
1990
|
+
* If a hashbang comment was passed as a token object from SourceCode,
|
1991
|
+
* its type will be "Shebang" because of the way ESLint itself handles hashbangs.
|
1992
|
+
* If a hashbang comment was passed in a string and then tokenized in this function,
|
1993
|
+
* its type will be "Hashbang" because of the way Espree tokenizes hashbangs.
|
1994
|
+
*/
|
1995
|
+
if (leftToken.type === "Shebang" || leftToken.type === "Hashbang") {
|
1990
1996
|
return false;
|
1991
1997
|
}
|
1992
1998
|
|
@@ -2007,7 +2013,7 @@ module.exports = {
|
|
2007
2013
|
if (comments.length) {
|
2008
2014
|
const firstComment = comments[0];
|
2009
2015
|
|
2010
|
-
if (firstComment.range[0] < rightToken.range[0]) {
|
2016
|
+
if (!rightToken || firstComment.range[0] < rightToken.range[0]) {
|
2011
2017
|
rightToken = firstComment;
|
2012
2018
|
}
|
2013
2019
|
}
|
package/lib/shared/logging.js
CHANGED
package/lib/shared/types.js
CHANGED
@@ -21,7 +21,7 @@ module.exports = {};
|
|
21
21
|
/**
|
22
22
|
* @typedef {Object} ParserOptions
|
23
23
|
* @property {EcmaFeatures} [ecmaFeatures] The optional features.
|
24
|
-
* @property {3|5|6|7|8|9|10|11|12|13|2015|2016|2017|2018|2019|2020|2021|2022} [ecmaVersion] The ECMAScript version (or revision number).
|
24
|
+
* @property {3|5|6|7|8|9|10|11|12|13|14|2015|2016|2017|2018|2019|2020|2021|2022|2023} [ecmaVersion] The ECMAScript version (or revision number).
|
25
25
|
* @property {"script"|"module"} [sourceType] The source code type.
|
26
26
|
* @property {boolean} [allowReserved] Allowing the use of reserved words as identifiers in ES3.
|
27
27
|
*/
|
@@ -69,7 +69,7 @@ module.exports = class Cursor {
|
|
69
69
|
* @returns {boolean} `true` if the next token exists.
|
70
70
|
* @abstract
|
71
71
|
*/
|
72
|
-
/*
|
72
|
+
/* c8 ignore next */
|
73
73
|
moveNext() { // eslint-disable-line class-methods-use-this -- Unused
|
74
74
|
throw new Error("Not implemented.");
|
75
75
|
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "eslint",
|
3
|
-
"version": "8.
|
3
|
+
"version": "8.23.1",
|
4
4
|
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
|
5
5
|
"description": "An AST-based pattern checker for JavaScript.",
|
6
6
|
"bin": {
|
@@ -39,7 +39,8 @@
|
|
39
39
|
"docs/src/rules/*.md": [
|
40
40
|
"node tools/fetch-docs-links.js",
|
41
41
|
"git add docs/src/_data/further_reading_links.json"
|
42
|
-
]
|
42
|
+
],
|
43
|
+
"docs/**/*.svg": "npx svgo -r --multipass"
|
43
44
|
},
|
44
45
|
"files": [
|
45
46
|
"LICENSE",
|
@@ -54,9 +55,10 @@
|
|
54
55
|
"homepage": "https://eslint.org",
|
55
56
|
"bugs": "https://github.com/eslint/eslint/issues/",
|
56
57
|
"dependencies": {
|
57
|
-
"@eslint/eslintrc": "^1.3.
|
58
|
+
"@eslint/eslintrc": "^1.3.2",
|
58
59
|
"@humanwhocodes/config-array": "^0.10.4",
|
59
60
|
"@humanwhocodes/gitignore-to-minimatch": "^1.0.2",
|
61
|
+
"@humanwhocodes/module-importer": "^1.0.1",
|
60
62
|
"ajv": "^6.10.0",
|
61
63
|
"chalk": "^4.0.0",
|
62
64
|
"cross-spawn": "^7.0.2",
|
@@ -66,13 +68,12 @@
|
|
66
68
|
"eslint-scope": "^7.1.1",
|
67
69
|
"eslint-utils": "^3.0.0",
|
68
70
|
"eslint-visitor-keys": "^3.3.0",
|
69
|
-
"espree": "^9.
|
71
|
+
"espree": "^9.4.0",
|
70
72
|
"esquery": "^1.4.0",
|
71
73
|
"esutils": "^2.0.2",
|
72
74
|
"fast-deep-equal": "^3.1.3",
|
73
75
|
"file-entry-cache": "^6.0.1",
|
74
76
|
"find-up": "^5.0.0",
|
75
|
-
"functional-red-black-tree": "^1.0.1",
|
76
77
|
"glob-parent": "^6.0.1",
|
77
78
|
"globals": "^13.15.0",
|
78
79
|
"globby": "^11.1.0",
|
@@ -81,6 +82,7 @@
|
|
81
82
|
"import-fresh": "^3.0.0",
|
82
83
|
"imurmurhash": "^0.1.4",
|
83
84
|
"is-glob": "^4.0.0",
|
85
|
+
"js-sdsl": "^4.1.4",
|
84
86
|
"js-yaml": "^4.1.0",
|
85
87
|
"json-stable-stringify-without-jsonify": "^1.0.1",
|
86
88
|
"levn": "^0.4.1",
|
@@ -91,13 +93,13 @@
|
|
91
93
|
"regexpp": "^3.2.0",
|
92
94
|
"strip-ansi": "^6.0.1",
|
93
95
|
"strip-json-comments": "^3.1.0",
|
94
|
-
"text-table": "^0.2.0"
|
95
|
-
"v8-compile-cache": "^2.0.3"
|
96
|
+
"text-table": "^0.2.0"
|
96
97
|
},
|
97
98
|
"devDependencies": {
|
98
99
|
"@babel/core": "^7.4.3",
|
99
100
|
"@babel/preset-env": "^7.4.3",
|
100
101
|
"babel-loader": "^8.0.5",
|
102
|
+
"c8": "^7.12.0",
|
101
103
|
"chai": "^4.0.1",
|
102
104
|
"cheerio": "^0.22.0",
|
103
105
|
"common-tags": "^1.8.0",
|
@@ -141,7 +143,6 @@
|
|
141
143
|
"mocha-junit-reporter": "^2.0.0",
|
142
144
|
"node-polyfill-webpack-plugin": "^1.0.3",
|
143
145
|
"npm-license": "^0.3.3",
|
144
|
-
"nyc": "^15.0.1",
|
145
146
|
"pirates": "^4.0.5",
|
146
147
|
"progress": "^2.0.3",
|
147
148
|
"proxyquire": "^2.0.1",
|