eslint 8.23.0 → 8.24.0
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/lib/cli-engine/file-enumerator.js +4 -2
- package/lib/eslint/eslint-helpers.js +27 -5
- 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/linter.js +12 -6
- package/lib/linter/timing.js +4 -4
- package/lib/rules/array-callback-return.js +2 -2
- 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/index.js +1 -0
- package/lib/rules/logical-assignment-operators.js +474 -0
- 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-labels.js +1 -1
- package/lib/rules/no-useless-computed-key.js +1 -1
- package/lib/rules/no-var.js +1 -1
- 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/strict.js +1 -1
- package/lib/rules/utils/ast-utils.js +1 -1
- package/lib/shared/logging.js +1 -1
- package/lib/source-code/token-store/cursor.js +1 -1
- package/package.json +5 -5
@@ -122,7 +122,8 @@ function statSafeSync(filePath) {
|
|
122
122
|
try {
|
123
123
|
return fs.statSync(filePath);
|
124
124
|
} catch (error) {
|
125
|
-
|
125
|
+
|
126
|
+
/* c8 ignore next */
|
126
127
|
if (error.code !== "ENOENT") {
|
127
128
|
throw error;
|
128
129
|
}
|
@@ -141,7 +142,8 @@ function readdirSafeSync(directoryPath) {
|
|
141
142
|
try {
|
142
143
|
return fs.readdirSync(directoryPath, { withFileTypes: true });
|
143
144
|
} catch (error) {
|
144
|
-
|
145
|
+
|
146
|
+
/* c8 ignore next */
|
145
147
|
if (error.code !== "ENOENT") {
|
146
148
|
throw error;
|
147
149
|
}
|
@@ -15,6 +15,7 @@ const fsp = fs.promises;
|
|
15
15
|
const isGlob = require("is-glob");
|
16
16
|
const globby = require("globby");
|
17
17
|
const hash = require("../cli-engine/hash");
|
18
|
+
const minimatch = require("minimatch");
|
18
19
|
|
19
20
|
//-----------------------------------------------------------------------------
|
20
21
|
// Errors
|
@@ -126,14 +127,14 @@ async function findFiles({
|
|
126
127
|
const filePaths = patterns.map(filePath => path.resolve(cwd, filePath));
|
127
128
|
const stats = await Promise.all(
|
128
129
|
filePaths.map(
|
129
|
-
filePath => fsp.stat(filePath).catch(() => {})
|
130
|
+
filePath => fsp.stat(filePath).catch(() => { })
|
130
131
|
)
|
131
132
|
);
|
132
133
|
|
133
134
|
stats.forEach((stat, index) => {
|
134
135
|
|
135
136
|
const filePath = filePaths[index];
|
136
|
-
const pattern = patterns[index];
|
137
|
+
const pattern = normalizeToPosix(patterns[index]);
|
137
138
|
|
138
139
|
if (stat) {
|
139
140
|
|
@@ -157,6 +158,11 @@ async function findFiles({
|
|
157
158
|
return false;
|
158
159
|
}
|
159
160
|
|
161
|
+
// patterns starting with ** always apply
|
162
|
+
if (filePattern.startsWith("**")) {
|
163
|
+
return true;
|
164
|
+
}
|
165
|
+
|
160
166
|
// patterns ending with * are not used for file search
|
161
167
|
if (filePattern.endsWith("*")) {
|
162
168
|
return false;
|
@@ -167,11 +173,27 @@ async function findFiles({
|
|
167
173
|
return false;
|
168
174
|
}
|
169
175
|
|
170
|
-
// check if the pattern would be inside the
|
176
|
+
// check if the pattern would be inside the config base path or not
|
171
177
|
const fullFilePattern = path.join(cwd, filePattern);
|
172
|
-
const
|
178
|
+
const patternRelativeToConfigBasePath = path.relative(configs.basePath, fullFilePattern);
|
179
|
+
|
180
|
+
if (patternRelativeToConfigBasePath.startsWith("..")) {
|
181
|
+
return false;
|
182
|
+
}
|
183
|
+
|
184
|
+
// check if the pattern matches
|
185
|
+
if (minimatch(filePath, path.dirname(fullFilePattern), { partial: true })) {
|
186
|
+
return true;
|
187
|
+
}
|
188
|
+
|
189
|
+
// check if the pattern is inside the directory or not
|
190
|
+
const patternRelativeToFilePath = path.relative(filePath, fullFilePattern);
|
191
|
+
|
192
|
+
if (patternRelativeToFilePath.startsWith("..")) {
|
193
|
+
return false;
|
194
|
+
}
|
173
195
|
|
174
|
-
return
|
196
|
+
return true;
|
175
197
|
})
|
176
198
|
.map(filePattern => {
|
177
199
|
if (filePattern.startsWith("**")) {
|
@@ -59,7 +59,7 @@ function getContinueContext(state, label) {
|
|
59
59
|
context = context.upper;
|
60
60
|
}
|
61
61
|
|
62
|
-
/*
|
62
|
+
/* c8 ignore next */
|
63
63
|
return null;
|
64
64
|
}
|
65
65
|
|
@@ -79,7 +79,7 @@ function getBreakContext(state, label) {
|
|
79
79
|
context = context.upper;
|
80
80
|
}
|
81
81
|
|
82
|
-
/*
|
82
|
+
/* c8 ignore next */
|
83
83
|
return null;
|
84
84
|
}
|
85
85
|
|
@@ -433,7 +433,7 @@ class CodePathState {
|
|
433
433
|
*/
|
434
434
|
return context;
|
435
435
|
|
436
|
-
/*
|
436
|
+
/* c8 ignore next */
|
437
437
|
default:
|
438
438
|
throw new Error("unreachable");
|
439
439
|
}
|
@@ -1030,7 +1030,7 @@ class CodePathState {
|
|
1030
1030
|
};
|
1031
1031
|
break;
|
1032
1032
|
|
1033
|
-
/*
|
1033
|
+
/* c8 ignore next */
|
1034
1034
|
default:
|
1035
1035
|
throw new Error(`unknown type: "${type}"`);
|
1036
1036
|
}
|
@@ -1095,7 +1095,7 @@ class CodePathState {
|
|
1095
1095
|
);
|
1096
1096
|
break;
|
1097
1097
|
|
1098
|
-
/*
|
1098
|
+
/* c8 ignore next */
|
1099
1099
|
default:
|
1100
1100
|
throw new Error("unreachable");
|
1101
1101
|
}
|
@@ -1392,11 +1392,12 @@ class CodePathState {
|
|
1392
1392
|
|
1393
1393
|
const context = getBreakContext(this, label);
|
1394
1394
|
|
1395
|
-
|
1395
|
+
|
1396
1396
|
if (context) {
|
1397
1397
|
context.brokenForkContext.add(forkContext.head);
|
1398
1398
|
}
|
1399
1399
|
|
1400
|
+
/* c8 ignore next */
|
1400
1401
|
forkContext.replaceHead(forkContext.makeUnreachable(-1, -1));
|
1401
1402
|
}
|
1402
1403
|
|
@@ -1417,7 +1418,6 @@ class CodePathState {
|
|
1417
1418
|
|
1418
1419
|
const context = getContinueContext(this, label);
|
1419
1420
|
|
1420
|
-
/* istanbul ignore else: foolproof (syntax error) */
|
1421
1421
|
if (context) {
|
1422
1422
|
if (context.continueDestSegments) {
|
1423
1423
|
makeLooped(this, forkContext.head, context.continueDestSegments);
|
@@ -20,7 +20,7 @@ const debug = require("debug")("eslint:code-path");
|
|
20
20
|
* @param {CodePathSegment} segment A segment to get.
|
21
21
|
* @returns {string} Id of the segment.
|
22
22
|
*/
|
23
|
-
/*
|
23
|
+
/* c8 ignore next */
|
24
24
|
function getId(segment) { // eslint-disable-line jsdoc/require-jsdoc -- Ignoring
|
25
25
|
return segment.id + (segment.reachable ? "" : "!");
|
26
26
|
}
|
@@ -67,7 +67,7 @@ module.exports = {
|
|
67
67
|
* @param {boolean} leaving A flag whether or not it's leaving
|
68
68
|
* @returns {void}
|
69
69
|
*/
|
70
|
-
dumpState: !debug.enabled ? debug : /*
|
70
|
+
dumpState: !debug.enabled ? debug : /* c8 ignore next */ function(node, state, leaving) {
|
71
71
|
for (let i = 0; i < state.currentSegments.length; ++i) {
|
72
72
|
const segInternal = state.currentSegments[i].internal;
|
73
73
|
|
@@ -98,7 +98,7 @@ module.exports = {
|
|
98
98
|
* @see http://www.graphviz.org
|
99
99
|
* @see http://www.webgraphviz.com
|
100
100
|
*/
|
101
|
-
dumpDot: !debug.enabled ? debug : /*
|
101
|
+
dumpDot: !debug.enabled ? debug : /* c8 ignore next */ function(codePath) {
|
102
102
|
let text =
|
103
103
|
"\n" +
|
104
104
|
"digraph {\n" +
|
package/lib/linter/linter.js
CHANGED
@@ -1601,12 +1601,18 @@ class Linter {
|
|
1601
1601
|
languageOptions.ecmaVersion
|
1602
1602
|
);
|
1603
1603
|
|
1604
|
-
|
1605
|
-
|
1606
|
-
|
1607
|
-
|
1608
|
-
|
1609
|
-
|
1604
|
+
/*
|
1605
|
+
* add configured globals and language globals
|
1606
|
+
*
|
1607
|
+
* using Object.assign instead of object spread for performance reasons
|
1608
|
+
* https://github.com/eslint/eslint/issues/16302
|
1609
|
+
*/
|
1610
|
+
const configuredGlobals = Object.assign(
|
1611
|
+
{},
|
1612
|
+
getGlobalsForEcmaVersion(languageOptions.ecmaVersion),
|
1613
|
+
languageOptions.sourceType === "commonjs" ? globals.commonjs : void 0,
|
1614
|
+
languageOptions.globals
|
1615
|
+
);
|
1610
1616
|
|
1611
1617
|
// double check that there is a parser to avoid mysterious error messages
|
1612
1618
|
if (!languageOptions.parser) {
|
package/lib/linter/timing.js
CHANGED
@@ -9,7 +9,7 @@
|
|
9
9
|
// Helpers
|
10
10
|
//------------------------------------------------------------------------------
|
11
11
|
|
12
|
-
/*
|
12
|
+
/* c8 ignore next */
|
13
13
|
/**
|
14
14
|
* Align the string to left
|
15
15
|
* @param {string} str string to evaluate
|
@@ -22,7 +22,7 @@ function alignLeft(str, len, ch) {
|
|
22
22
|
return str + new Array(len - str.length + 1).join(ch || " ");
|
23
23
|
}
|
24
24
|
|
25
|
-
/*
|
25
|
+
/* c8 ignore next */
|
26
26
|
/**
|
27
27
|
* Align the string to right
|
28
28
|
* @param {string} str string to evaluate
|
@@ -64,7 +64,7 @@ function getListSize() {
|
|
64
64
|
return TIMING_ENV_VAR_AS_INTEGER > 10 ? TIMING_ENV_VAR_AS_INTEGER : MINIMUM_SIZE;
|
65
65
|
}
|
66
66
|
|
67
|
-
/*
|
67
|
+
/* c8 ignore next */
|
68
68
|
/**
|
69
69
|
* display the data
|
70
70
|
* @param {Object} data Data object to be displayed
|
@@ -119,7 +119,7 @@ function display(data) {
|
|
119
119
|
console.log(table.join("\n")); // eslint-disable-line no-console -- Debugging function
|
120
120
|
}
|
121
121
|
|
122
|
-
/*
|
122
|
+
/* c8 ignore next */
|
123
123
|
module.exports = (function() {
|
124
124
|
|
125
125
|
const data = Object.create(null);
|
@@ -16,7 +16,7 @@ const astUtils = require("./utils/ast-utils");
|
|
16
16
|
//------------------------------------------------------------------------------
|
17
17
|
|
18
18
|
const TARGET_NODE_TYPE = /^(?:Arrow)?FunctionExpression$/u;
|
19
|
-
const TARGET_METHODS = /^(?:every|filter|find(?:Index)?|flatMap|forEach|map|reduce(?:Right)?|some|sort)$/u;
|
19
|
+
const TARGET_METHODS = /^(?:every|filter|find(?:Last)?(?:Index)?|flatMap|forEach|map|reduce(?:Right)?|some|sort)$/u;
|
20
20
|
|
21
21
|
/**
|
22
22
|
* Checks a given code path segment is reachable.
|
@@ -125,7 +125,7 @@ function getArrayMethodName(node) {
|
|
125
125
|
}
|
126
126
|
}
|
127
127
|
|
128
|
-
/*
|
128
|
+
/* c8 ignore next */
|
129
129
|
return null;
|
130
130
|
}
|
131
131
|
|
@@ -28,10 +28,11 @@ function findReference(scope, node) {
|
|
28
28
|
const references = scope.references.filter(reference => reference.identifier.range[0] === node.range[0] &&
|
29
29
|
reference.identifier.range[1] === node.range[1]);
|
30
30
|
|
31
|
-
/* istanbul ignore else: correctly returns null */
|
32
31
|
if (references.length === 1) {
|
33
32
|
return references[0];
|
34
33
|
}
|
34
|
+
|
35
|
+
/* c8 ignore next */
|
35
36
|
return null;
|
36
37
|
|
37
38
|
}
|
@@ -18,8 +18,8 @@ const astUtils = require("./utils/ast-utils");
|
|
18
18
|
//------------------------------------------------------------------------------
|
19
19
|
// Rule Definition
|
20
20
|
//------------------------------------------------------------------------------
|
21
|
-
|
22
|
-
/*
|
21
|
+
// this rule has known coverage issues, but it's deprecated and shouldn't be updated in the future anyway.
|
22
|
+
/* c8 ignore next */
|
23
23
|
/** @type {import('../shared/types').Rule} */
|
24
24
|
module.exports = {
|
25
25
|
meta: {
|
@@ -212,10 +212,10 @@ module.exports = {
|
|
212
212
|
if (context.options[0] === "tab") {
|
213
213
|
indentSize = 1;
|
214
214
|
indentType = "tab";
|
215
|
-
} else /*
|
215
|
+
} else /* c8 ignore start */ if (typeof context.options[0] === "number") {
|
216
216
|
indentSize = context.options[0];
|
217
217
|
indentType = "space";
|
218
|
-
}
|
218
|
+
}/* c8 ignore stop */
|
219
219
|
|
220
220
|
if (context.options[1]) {
|
221
221
|
const opts = context.options[1];
|
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/index.js
CHANGED
@@ -72,6 +72,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
|
|
72
72
|
"lines-around-comment": () => require("./lines-around-comment"),
|
73
73
|
"lines-around-directive": () => require("./lines-around-directive"),
|
74
74
|
"lines-between-class-members": () => require("./lines-between-class-members"),
|
75
|
+
"logical-assignment-operators": () => require("./logical-assignment-operators"),
|
75
76
|
"max-classes-per-file": () => require("./max-classes-per-file"),
|
76
77
|
"max-depth": () => require("./max-depth"),
|
77
78
|
"max-len": () => require("./max-len"),
|
@@ -0,0 +1,474 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Rule to replace assignment expressions with logical operator assignment
|
3
|
+
* @author Daniel Martens
|
4
|
+
*/
|
5
|
+
"use strict";
|
6
|
+
|
7
|
+
//------------------------------------------------------------------------------
|
8
|
+
// Requirements
|
9
|
+
//------------------------------------------------------------------------------
|
10
|
+
const astUtils = require("./utils/ast-utils.js");
|
11
|
+
|
12
|
+
//------------------------------------------------------------------------------
|
13
|
+
// Helpers
|
14
|
+
//------------------------------------------------------------------------------
|
15
|
+
|
16
|
+
const baseTypes = new Set(["Identifier", "Super", "ThisExpression"]);
|
17
|
+
|
18
|
+
/**
|
19
|
+
* Returns true iff either "undefined" or a void expression (eg. "void 0")
|
20
|
+
* @param {ASTNode} expression Expression to check
|
21
|
+
* @param {import('eslint-scope').Scope} scope Scope of the expression
|
22
|
+
* @returns {boolean} True iff "undefined" or "void ..."
|
23
|
+
*/
|
24
|
+
function isUndefined(expression, scope) {
|
25
|
+
if (expression.type === "Identifier" && expression.name === "undefined") {
|
26
|
+
return astUtils.isReferenceToGlobalVariable(scope, expression);
|
27
|
+
}
|
28
|
+
|
29
|
+
return expression.type === "UnaryExpression" &&
|
30
|
+
expression.operator === "void" &&
|
31
|
+
expression.argument.type === "Literal" &&
|
32
|
+
expression.argument.value === 0;
|
33
|
+
}
|
34
|
+
|
35
|
+
/**
|
36
|
+
* Returns true iff the reference is either an identifier or member expression
|
37
|
+
* @param {ASTNode} expression Expression to check
|
38
|
+
* @returns {boolean} True for identifiers and member expressions
|
39
|
+
*/
|
40
|
+
function isReference(expression) {
|
41
|
+
return (expression.type === "Identifier" && expression.name !== "undefined") ||
|
42
|
+
expression.type === "MemberExpression";
|
43
|
+
}
|
44
|
+
|
45
|
+
/**
|
46
|
+
* Returns true iff the expression checks for nullish with loose equals.
|
47
|
+
* Examples: value == null, value == void 0
|
48
|
+
* @param {ASTNode} expression Test condition
|
49
|
+
* @param {import('eslint-scope').Scope} scope Scope of the expression
|
50
|
+
* @returns {boolean} True iff implicit nullish comparison
|
51
|
+
*/
|
52
|
+
function isImplicitNullishComparison(expression, scope) {
|
53
|
+
if (expression.type !== "BinaryExpression" || expression.operator !== "==") {
|
54
|
+
return false;
|
55
|
+
}
|
56
|
+
|
57
|
+
const reference = isReference(expression.left) ? "left" : "right";
|
58
|
+
const nullish = reference === "left" ? "right" : "left";
|
59
|
+
|
60
|
+
return isReference(expression[reference]) &&
|
61
|
+
(astUtils.isNullLiteral(expression[nullish]) || isUndefined(expression[nullish], scope));
|
62
|
+
}
|
63
|
+
|
64
|
+
/**
|
65
|
+
* Condition with two equal comparisons.
|
66
|
+
* @param {ASTNode} expression Condition
|
67
|
+
* @returns {boolean} True iff matches ? === ? || ? === ?
|
68
|
+
*/
|
69
|
+
function isDoubleComparison(expression) {
|
70
|
+
return expression.type === "LogicalExpression" &&
|
71
|
+
expression.operator === "||" &&
|
72
|
+
expression.left.type === "BinaryExpression" &&
|
73
|
+
expression.left.operator === "===" &&
|
74
|
+
expression.right.type === "BinaryExpression" &&
|
75
|
+
expression.right.operator === "===";
|
76
|
+
}
|
77
|
+
|
78
|
+
/**
|
79
|
+
* Returns true iff the expression checks for undefined and null.
|
80
|
+
* Example: value === null || value === undefined
|
81
|
+
* @param {ASTNode} expression Test condition
|
82
|
+
* @param {import('eslint-scope').Scope} scope Scope of the expression
|
83
|
+
* @returns {boolean} True iff explicit nullish comparison
|
84
|
+
*/
|
85
|
+
function isExplicitNullishComparison(expression, scope) {
|
86
|
+
if (!isDoubleComparison(expression)) {
|
87
|
+
return false;
|
88
|
+
}
|
89
|
+
const leftReference = isReference(expression.left.left) ? "left" : "right";
|
90
|
+
const leftNullish = leftReference === "left" ? "right" : "left";
|
91
|
+
const rightReference = isReference(expression.right.left) ? "left" : "right";
|
92
|
+
const rightNullish = rightReference === "left" ? "right" : "left";
|
93
|
+
|
94
|
+
return astUtils.isSameReference(expression.left[leftReference], expression.right[rightReference]) &&
|
95
|
+
((astUtils.isNullLiteral(expression.left[leftNullish]) && isUndefined(expression.right[rightNullish], scope)) ||
|
96
|
+
(isUndefined(expression.left[leftNullish], scope) && astUtils.isNullLiteral(expression.right[rightNullish])));
|
97
|
+
}
|
98
|
+
|
99
|
+
/**
|
100
|
+
* Returns true for Boolean(arg) calls
|
101
|
+
* @param {ASTNode} expression Test condition
|
102
|
+
* @param {import('eslint-scope').Scope} scope Scope of the expression
|
103
|
+
* @returns {boolean} Whether the expression is a boolean cast
|
104
|
+
*/
|
105
|
+
function isBooleanCast(expression, scope) {
|
106
|
+
return expression.type === "CallExpression" &&
|
107
|
+
expression.callee.name === "Boolean" &&
|
108
|
+
expression.arguments.length === 1 &&
|
109
|
+
astUtils.isReferenceToGlobalVariable(scope, expression.callee);
|
110
|
+
}
|
111
|
+
|
112
|
+
/**
|
113
|
+
* Returns true for:
|
114
|
+
* truthiness checks: value, Boolean(value), !!value
|
115
|
+
* falsyness checks: !value, !Boolean(value)
|
116
|
+
* nullish checks: value == null, value === undefined || value === null
|
117
|
+
* @param {ASTNode} expression Test condition
|
118
|
+
* @param {import('eslint-scope').Scope} scope Scope of the expression
|
119
|
+
* @returns {?{ reference: ASTNode, operator: '??'|'||'|'&&'}} Null if not a known existence
|
120
|
+
*/
|
121
|
+
function getExistence(expression, scope) {
|
122
|
+
const isNegated = expression.type === "UnaryExpression" && expression.operator === "!";
|
123
|
+
const base = isNegated ? expression.argument : expression;
|
124
|
+
|
125
|
+
switch (true) {
|
126
|
+
case isReference(base):
|
127
|
+
return { reference: base, operator: isNegated ? "||" : "&&" };
|
128
|
+
case base.type === "UnaryExpression" && base.operator === "!" && isReference(base.argument):
|
129
|
+
return { reference: base.argument, operator: "&&" };
|
130
|
+
case isBooleanCast(base, scope) && isReference(base.arguments[0]):
|
131
|
+
return { reference: base.arguments[0], operator: isNegated ? "||" : "&&" };
|
132
|
+
case isImplicitNullishComparison(expression, scope):
|
133
|
+
return { reference: isReference(expression.left) ? expression.left : expression.right, operator: "??" };
|
134
|
+
case isExplicitNullishComparison(expression, scope):
|
135
|
+
return { reference: isReference(expression.left.left) ? expression.left.left : expression.left.right, operator: "??" };
|
136
|
+
default: return null;
|
137
|
+
}
|
138
|
+
}
|
139
|
+
|
140
|
+
/**
|
141
|
+
* Returns true iff the node is inside a with block
|
142
|
+
* @param {ASTNode} node Node to check
|
143
|
+
* @returns {boolean} True iff passed node is inside a with block
|
144
|
+
*/
|
145
|
+
function isInsideWithBlock(node) {
|
146
|
+
if (node.type === "Program") {
|
147
|
+
return false;
|
148
|
+
}
|
149
|
+
|
150
|
+
return node.parent.type === "WithStatement" && node.parent.body === node ? true : isInsideWithBlock(node.parent);
|
151
|
+
}
|
152
|
+
|
153
|
+
//------------------------------------------------------------------------------
|
154
|
+
// Rule Definition
|
155
|
+
//------------------------------------------------------------------------------
|
156
|
+
/** @type {import('../shared/types').Rule} */
|
157
|
+
module.exports = {
|
158
|
+
meta: {
|
159
|
+
type: "suggestion",
|
160
|
+
|
161
|
+
docs: {
|
162
|
+
description: "Require or disallow logical assignment logical operator shorthand",
|
163
|
+
recommended: false,
|
164
|
+
url: "https://eslint.org/docs/rules/logical-assignment-operators"
|
165
|
+
},
|
166
|
+
|
167
|
+
schema: {
|
168
|
+
type: "array",
|
169
|
+
oneOf: [{
|
170
|
+
items: [
|
171
|
+
{ const: "always" },
|
172
|
+
{
|
173
|
+
type: "object",
|
174
|
+
properties: {
|
175
|
+
enforceForIfStatements: {
|
176
|
+
type: "boolean"
|
177
|
+
}
|
178
|
+
},
|
179
|
+
additionalProperties: false
|
180
|
+
}
|
181
|
+
],
|
182
|
+
minItems: 0, // 0 for allowing passing no options
|
183
|
+
maxItems: 2
|
184
|
+
}, {
|
185
|
+
items: [{ const: "never" }],
|
186
|
+
minItems: 1,
|
187
|
+
maxItems: 1
|
188
|
+
}]
|
189
|
+
},
|
190
|
+
fixable: "code",
|
191
|
+
// eslint-disable-next-line eslint-plugin/require-meta-has-suggestions -- Does not detect conditional suggestions
|
192
|
+
hasSuggestions: true,
|
193
|
+
messages: {
|
194
|
+
assignment: "Assignment (=) can be replaced with operator assignment ({{operator}}).",
|
195
|
+
useLogicalOperator: "Convert this assignment to use the operator {{ operator }}.",
|
196
|
+
logical: "Logical expression can be replaced with an assignment ({{ operator }}).",
|
197
|
+
convertLogical: "Replace this logical expression with an assignment with the operator {{ operator }}.",
|
198
|
+
if: "'if' statement can be replaced with a logical operator assignment with operator {{ operator }}.",
|
199
|
+
convertIf: "Replace this 'if' statement with a logical assignment with operator {{ operator }}.",
|
200
|
+
unexpected: "Unexpected logical operator assignment ({{operator}}) shorthand.",
|
201
|
+
separate: "Separate the logical assignment into an assignment with a logical operator."
|
202
|
+
}
|
203
|
+
},
|
204
|
+
|
205
|
+
create(context) {
|
206
|
+
const mode = context.options[0] === "never" ? "never" : "always";
|
207
|
+
const checkIf = mode === "always" && context.options.length > 1 && context.options[1].enforceForIfStatements;
|
208
|
+
const sourceCode = context.getSourceCode();
|
209
|
+
const isStrict = context.getScope().isStrict;
|
210
|
+
|
211
|
+
/**
|
212
|
+
* Returns false if the access could be a getter
|
213
|
+
* @param {ASTNode} node Assignment expression
|
214
|
+
* @returns {boolean} True iff the fix is safe
|
215
|
+
*/
|
216
|
+
function cannotBeGetter(node) {
|
217
|
+
return node.type === "Identifier" &&
|
218
|
+
(isStrict || !isInsideWithBlock(node));
|
219
|
+
}
|
220
|
+
|
221
|
+
/**
|
222
|
+
* Check whether only a single property is accessed
|
223
|
+
* @param {ASTNode} node reference
|
224
|
+
* @returns {boolean} True iff a single property is accessed
|
225
|
+
*/
|
226
|
+
function accessesSingleProperty(node) {
|
227
|
+
if (!isStrict && isInsideWithBlock(node)) {
|
228
|
+
return node.type === "Identifier";
|
229
|
+
}
|
230
|
+
|
231
|
+
return node.type === "MemberExpression" &&
|
232
|
+
baseTypes.has(node.object.type) &&
|
233
|
+
(!node.computed || (node.property.type !== "MemberExpression" && node.property.type !== "ChainExpression"));
|
234
|
+
}
|
235
|
+
|
236
|
+
/**
|
237
|
+
* Adds a fixer or suggestion whether on the fix is safe.
|
238
|
+
* @param {{ messageId: string, node: ASTNode }} descriptor Report descriptor without fix or suggest
|
239
|
+
* @param {{ messageId: string, fix: Function }} suggestion Adds the fix or the whole suggestion as only element in "suggest" to suggestion
|
240
|
+
* @param {boolean} shouldBeFixed Fix iff the condition is true
|
241
|
+
* @returns {Object} Descriptor with either an added fix or suggestion
|
242
|
+
*/
|
243
|
+
function createConditionalFixer(descriptor, suggestion, shouldBeFixed) {
|
244
|
+
if (shouldBeFixed) {
|
245
|
+
return {
|
246
|
+
...descriptor,
|
247
|
+
fix: suggestion.fix
|
248
|
+
};
|
249
|
+
}
|
250
|
+
|
251
|
+
return {
|
252
|
+
...descriptor,
|
253
|
+
suggest: [suggestion]
|
254
|
+
};
|
255
|
+
}
|
256
|
+
|
257
|
+
|
258
|
+
/**
|
259
|
+
* Returns the operator token for assignments and binary expressions
|
260
|
+
* @param {ASTNode} node AssignmentExpression or BinaryExpression
|
261
|
+
* @returns {import('eslint').AST.Token} Operator token between the left and right expression
|
262
|
+
*/
|
263
|
+
function getOperatorToken(node) {
|
264
|
+
return sourceCode.getFirstTokenBetween(node.left, node.right, token => token.value === node.operator);
|
265
|
+
}
|
266
|
+
|
267
|
+
if (mode === "never") {
|
268
|
+
return {
|
269
|
+
|
270
|
+
// foo ||= bar
|
271
|
+
"AssignmentExpression"(assignment) {
|
272
|
+
if (!astUtils.isLogicalAssignmentOperator(assignment.operator)) {
|
273
|
+
return;
|
274
|
+
}
|
275
|
+
|
276
|
+
const descriptor = {
|
277
|
+
messageId: "unexpected",
|
278
|
+
node: assignment,
|
279
|
+
data: { operator: assignment.operator }
|
280
|
+
};
|
281
|
+
const suggestion = {
|
282
|
+
messageId: "separate",
|
283
|
+
*fix(ruleFixer) {
|
284
|
+
if (sourceCode.getCommentsInside(assignment).length > 0) {
|
285
|
+
return;
|
286
|
+
}
|
287
|
+
|
288
|
+
const operatorToken = getOperatorToken(assignment);
|
289
|
+
|
290
|
+
// -> foo = bar
|
291
|
+
yield ruleFixer.replaceText(operatorToken, "=");
|
292
|
+
|
293
|
+
const assignmentText = sourceCode.getText(assignment.left);
|
294
|
+
const operator = assignment.operator.slice(0, -1);
|
295
|
+
|
296
|
+
// -> foo = foo || bar
|
297
|
+
yield ruleFixer.insertTextAfter(operatorToken, ` ${assignmentText} ${operator}`);
|
298
|
+
|
299
|
+
const precedence = astUtils.getPrecedence(assignment.right) <= astUtils.getPrecedence({ type: "LogicalExpression", operator });
|
300
|
+
|
301
|
+
// ?? and || / && cannot be mixed but have same precedence
|
302
|
+
const mixed = assignment.operator === "??=" && astUtils.isLogicalExpression(assignment.right);
|
303
|
+
|
304
|
+
if (!astUtils.isParenthesised(sourceCode, assignment.right) && (precedence || mixed)) {
|
305
|
+
|
306
|
+
// -> foo = foo || (bar)
|
307
|
+
yield ruleFixer.insertTextBefore(assignment.right, "(");
|
308
|
+
yield ruleFixer.insertTextAfter(assignment.right, ")");
|
309
|
+
}
|
310
|
+
}
|
311
|
+
};
|
312
|
+
|
313
|
+
context.report(createConditionalFixer(descriptor, suggestion, cannotBeGetter(assignment.left)));
|
314
|
+
}
|
315
|
+
};
|
316
|
+
}
|
317
|
+
|
318
|
+
return {
|
319
|
+
|
320
|
+
// foo = foo || bar
|
321
|
+
"AssignmentExpression[operator='='][right.type='LogicalExpression']"(assignment) {
|
322
|
+
if (!astUtils.isSameReference(assignment.left, assignment.right.left)) {
|
323
|
+
return;
|
324
|
+
}
|
325
|
+
|
326
|
+
const descriptor = {
|
327
|
+
messageId: "assignment",
|
328
|
+
node: assignment,
|
329
|
+
data: { operator: `${assignment.right.operator}=` }
|
330
|
+
};
|
331
|
+
const suggestion = {
|
332
|
+
messageId: "useLogicalOperator",
|
333
|
+
data: { operator: `${assignment.right.operator}=` },
|
334
|
+
*fix(ruleFixer) {
|
335
|
+
if (sourceCode.getCommentsInside(assignment).length > 0) {
|
336
|
+
return;
|
337
|
+
}
|
338
|
+
|
339
|
+
// No need for parenthesis around the assignment based on precedence as the precedence stays the same even with changed operator
|
340
|
+
const assignmentOperatorToken = getOperatorToken(assignment);
|
341
|
+
|
342
|
+
// -> foo ||= foo || bar
|
343
|
+
yield ruleFixer.insertTextBefore(assignmentOperatorToken, assignment.right.operator);
|
344
|
+
|
345
|
+
// -> foo ||= bar
|
346
|
+
const logicalOperatorToken = getOperatorToken(assignment.right);
|
347
|
+
const firstRightOperandToken = sourceCode.getTokenAfter(logicalOperatorToken);
|
348
|
+
|
349
|
+
yield ruleFixer.removeRange([assignment.right.range[0], firstRightOperandToken.range[0]]);
|
350
|
+
}
|
351
|
+
};
|
352
|
+
|
353
|
+
context.report(createConditionalFixer(descriptor, suggestion, cannotBeGetter(assignment.left)));
|
354
|
+
},
|
355
|
+
|
356
|
+
// foo || (foo = bar)
|
357
|
+
'LogicalExpression[right.type="AssignmentExpression"][right.operator="="]'(logical) {
|
358
|
+
|
359
|
+
// Right side has to be parenthesized, otherwise would be parsed as (foo || foo) = bar which is illegal
|
360
|
+
if (isReference(logical.left) && astUtils.isSameReference(logical.left, logical.right.left)) {
|
361
|
+
const descriptor = {
|
362
|
+
messageId: "logical",
|
363
|
+
node: logical,
|
364
|
+
data: { operator: `${logical.operator}=` }
|
365
|
+
};
|
366
|
+
const suggestion = {
|
367
|
+
messageId: "convertLogical",
|
368
|
+
data: { operator: `${logical.operator}=` },
|
369
|
+
*fix(ruleFixer) {
|
370
|
+
if (sourceCode.getCommentsInside(logical).length > 0) {
|
371
|
+
return;
|
372
|
+
}
|
373
|
+
|
374
|
+
const requiresOuterParenthesis = logical.parent.type !== "ExpressionStatement" &&
|
375
|
+
(astUtils.getPrecedence({ type: "AssignmentExpression" }) < astUtils.getPrecedence(logical.parent));
|
376
|
+
|
377
|
+
if (!astUtils.isParenthesised(sourceCode, logical) && requiresOuterParenthesis) {
|
378
|
+
yield ruleFixer.insertTextBefore(logical, "(");
|
379
|
+
yield ruleFixer.insertTextAfter(logical, ")");
|
380
|
+
}
|
381
|
+
|
382
|
+
// Also removes all opening parenthesis
|
383
|
+
yield ruleFixer.removeRange([logical.range[0], logical.right.range[0]]); // -> foo = bar)
|
384
|
+
|
385
|
+
// Also removes all ending parenthesis
|
386
|
+
yield ruleFixer.removeRange([logical.right.range[1], logical.range[1]]); // -> foo = bar
|
387
|
+
|
388
|
+
const operatorToken = getOperatorToken(logical.right);
|
389
|
+
|
390
|
+
yield ruleFixer.insertTextBefore(operatorToken, logical.operator); // -> foo ||= bar
|
391
|
+
}
|
392
|
+
};
|
393
|
+
const fix = cannotBeGetter(logical.left) || accessesSingleProperty(logical.left);
|
394
|
+
|
395
|
+
context.report(createConditionalFixer(descriptor, suggestion, fix));
|
396
|
+
}
|
397
|
+
},
|
398
|
+
|
399
|
+
// if (foo) foo = bar
|
400
|
+
"IfStatement[alternate=null]"(ifNode) {
|
401
|
+
if (!checkIf) {
|
402
|
+
return;
|
403
|
+
}
|
404
|
+
|
405
|
+
const hasBody = ifNode.consequent.type === "BlockStatement";
|
406
|
+
|
407
|
+
if (hasBody && ifNode.consequent.body.length !== 1) {
|
408
|
+
return;
|
409
|
+
}
|
410
|
+
|
411
|
+
const body = hasBody ? ifNode.consequent.body[0] : ifNode.consequent;
|
412
|
+
const scope = context.getScope();
|
413
|
+
const existence = getExistence(ifNode.test, scope);
|
414
|
+
|
415
|
+
if (
|
416
|
+
body.type === "ExpressionStatement" &&
|
417
|
+
body.expression.type === "AssignmentExpression" &&
|
418
|
+
body.expression.operator === "=" &&
|
419
|
+
existence !== null &&
|
420
|
+
astUtils.isSameReference(existence.reference, body.expression.left)
|
421
|
+
) {
|
422
|
+
const descriptor = {
|
423
|
+
messageId: "if",
|
424
|
+
node: ifNode,
|
425
|
+
data: { operator: `${existence.operator}=` }
|
426
|
+
};
|
427
|
+
const suggestion = {
|
428
|
+
messageId: "convertIf",
|
429
|
+
data: { operator: `${existence.operator}=` },
|
430
|
+
*fix(ruleFixer) {
|
431
|
+
if (sourceCode.getCommentsInside(ifNode).length > 0) {
|
432
|
+
return;
|
433
|
+
}
|
434
|
+
|
435
|
+
const firstBodyToken = sourceCode.getFirstToken(body);
|
436
|
+
const prevToken = sourceCode.getTokenBefore(ifNode);
|
437
|
+
|
438
|
+
if (
|
439
|
+
prevToken !== null &&
|
440
|
+
prevToken.value !== ";" &&
|
441
|
+
prevToken.value !== "{" &&
|
442
|
+
firstBodyToken.type !== "Identifier" &&
|
443
|
+
firstBodyToken.type !== "Keyword"
|
444
|
+
) {
|
445
|
+
|
446
|
+
// Do not fix if the fixed statement could be part of the previous statement (eg. fn() if (a == null) (a) = b --> fn()(a) ??= b)
|
447
|
+
return;
|
448
|
+
}
|
449
|
+
|
450
|
+
|
451
|
+
const operatorToken = getOperatorToken(body.expression);
|
452
|
+
|
453
|
+
yield ruleFixer.insertTextBefore(operatorToken, existence.operator); // -> if (foo) foo ||= bar
|
454
|
+
|
455
|
+
yield ruleFixer.removeRange([ifNode.range[0], body.range[0]]); // -> foo ||= bar
|
456
|
+
|
457
|
+
yield ruleFixer.removeRange([body.range[1], ifNode.range[1]]); // -> foo ||= bar, only present if "if" had a body
|
458
|
+
|
459
|
+
const nextToken = sourceCode.getTokenAfter(body.expression);
|
460
|
+
|
461
|
+
if (hasBody && (nextToken !== null && nextToken.value !== ";")) {
|
462
|
+
yield ruleFixer.insertTextAfter(ifNode, ";");
|
463
|
+
}
|
464
|
+
}
|
465
|
+
};
|
466
|
+
const shouldBeFixed = cannotBeGetter(existence.reference) ||
|
467
|
+
(ifNode.test.type !== "LogicalExpression" && accessesSingleProperty(existence.reference));
|
468
|
+
|
469
|
+
context.report(createConditionalFixer(descriptor, suggestion, shouldBeFixed));
|
470
|
+
}
|
471
|
+
}
|
472
|
+
};
|
473
|
+
}
|
474
|
+
};
|
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
|
}
|
package/lib/rules/no-labels.js
CHANGED
package/lib/rules/no-var.js
CHANGED
@@ -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
|
|
package/lib/rules/strict.js
CHANGED
@@ -105,7 +105,7 @@ module.exports = {
|
|
105
105
|
if (ecmaFeatures.impliedStrict) {
|
106
106
|
mode = "implied";
|
107
107
|
} else if (mode === "safe") {
|
108
|
-
mode = ecmaFeatures.globalReturn ? "global" : "function";
|
108
|
+
mode = ecmaFeatures.globalReturn || context.languageOptions.sourceType === "commonjs" ? "global" : "function";
|
109
109
|
}
|
110
110
|
|
111
111
|
/**
|
package/lib/shared/logging.js
CHANGED
@@ -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.24.0",
|
4
4
|
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
|
5
5
|
"description": "An AST-based pattern checker for JavaScript.",
|
6
6
|
"bin": {
|
@@ -55,8 +55,8 @@
|
|
55
55
|
"homepage": "https://eslint.org",
|
56
56
|
"bugs": "https://github.com/eslint/eslint/issues/",
|
57
57
|
"dependencies": {
|
58
|
-
"@eslint/eslintrc": "^1.3.
|
59
|
-
"@humanwhocodes/config-array": "^0.10.
|
58
|
+
"@eslint/eslintrc": "^1.3.2",
|
59
|
+
"@humanwhocodes/config-array": "^0.10.5",
|
60
60
|
"@humanwhocodes/gitignore-to-minimatch": "^1.0.2",
|
61
61
|
"@humanwhocodes/module-importer": "^1.0.1",
|
62
62
|
"ajv": "^6.10.0",
|
@@ -74,7 +74,6 @@
|
|
74
74
|
"fast-deep-equal": "^3.1.3",
|
75
75
|
"file-entry-cache": "^6.0.1",
|
76
76
|
"find-up": "^5.0.0",
|
77
|
-
"functional-red-black-tree": "^1.0.1",
|
78
77
|
"glob-parent": "^6.0.1",
|
79
78
|
"globals": "^13.15.0",
|
80
79
|
"globby": "^11.1.0",
|
@@ -83,6 +82,7 @@
|
|
83
82
|
"import-fresh": "^3.0.0",
|
84
83
|
"imurmurhash": "^0.1.4",
|
85
84
|
"is-glob": "^4.0.0",
|
85
|
+
"js-sdsl": "^4.1.4",
|
86
86
|
"js-yaml": "^4.1.0",
|
87
87
|
"json-stable-stringify-without-jsonify": "^1.0.1",
|
88
88
|
"levn": "^0.4.1",
|
@@ -99,6 +99,7 @@
|
|
99
99
|
"@babel/core": "^7.4.3",
|
100
100
|
"@babel/preset-env": "^7.4.3",
|
101
101
|
"babel-loader": "^8.0.5",
|
102
|
+
"c8": "^7.12.0",
|
102
103
|
"chai": "^4.0.1",
|
103
104
|
"cheerio": "^0.22.0",
|
104
105
|
"common-tags": "^1.8.0",
|
@@ -142,7 +143,6 @@
|
|
142
143
|
"mocha-junit-reporter": "^2.0.0",
|
143
144
|
"node-polyfill-webpack-plugin": "^1.0.3",
|
144
145
|
"npm-license": "^0.3.3",
|
145
|
-
"nyc": "^15.0.1",
|
146
146
|
"pirates": "^4.0.5",
|
147
147
|
"progress": "^2.0.3",
|
148
148
|
"proxyquire": "^2.0.1",
|