eslint 9.0.0-rc.0 → 9.0.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.
@@ -9,22 +9,6 @@
9
9
  // Helpers
10
10
  //------------------------------------------------------------------------------
11
11
 
12
- /**
13
- * Checks all segments in a set and returns true if any are reachable.
14
- * @param {Set<CodePathSegment>} segments The segments to check.
15
- * @returns {boolean} True if any segment is reachable; false otherwise.
16
- */
17
- function isAnySegmentReachable(segments) {
18
-
19
- for (const segment of segments) {
20
- if (segment.reachable) {
21
- return true;
22
- }
23
- }
24
-
25
- return false;
26
- }
27
-
28
12
  /**
29
13
  * Checks whether or not a given node is a constructor.
30
14
  * @param {ASTNode} node A node to check. This node type is one of
@@ -165,8 +149,7 @@ module.exports = {
165
149
  missingAll: "Expected to call 'super()'.",
166
150
 
167
151
  duplicate: "Unexpected duplicate 'super()'.",
168
- badSuper: "Unexpected 'super()' because 'super' is not a constructor.",
169
- unexpected: "Unexpected 'super()'."
152
+ badSuper: "Unexpected 'super()' because 'super' is not a constructor."
170
153
  }
171
154
  },
172
155
 
@@ -186,7 +169,7 @@ module.exports = {
186
169
  /**
187
170
  * @type {Record<string, SegmentInfo>}
188
171
  */
189
- let segInfoMap = Object.create(null);
172
+ const segInfoMap = Object.create(null);
190
173
 
191
174
  /**
192
175
  * Gets the flag which shows `super()` is called in some paths.
@@ -194,7 +177,7 @@ module.exports = {
194
177
  * @returns {boolean} The flag which shows `super()` is called in some paths
195
178
  */
196
179
  function isCalledInSomePath(segment) {
197
- return segment.reachable && segInfoMap[segment.id]?.calledInSomePaths;
180
+ return segment.reachable && segInfoMap[segment.id].calledInSomePaths;
198
181
  }
199
182
 
200
183
  /**
@@ -212,17 +195,6 @@ module.exports = {
212
195
  * @returns {boolean} The flag which shows `super()` is called in all paths.
213
196
  */
214
197
  function isCalledInEveryPath(segment) {
215
-
216
- /*
217
- * If specific segment is the looped segment of the current segment,
218
- * skip the segment.
219
- * If not skipped, this never becomes true after a loop.
220
- */
221
- if (segment.nextSegments.length === 1 &&
222
- segment.nextSegments[0]?.isLoopedPrevSegment(segment)) {
223
- return true;
224
- }
225
-
226
198
  return segment.reachable && segInfoMap[segment.id].calledInEveryPaths;
227
199
  }
228
200
 
@@ -279,9 +251,9 @@ module.exports = {
279
251
  }
280
252
 
281
253
  // Reports if `super()` lacked.
282
- const seenSegments = codePath.returnedSegments.filter(hasSegmentBeenSeen);
283
- const calledInEveryPaths = seenSegments.every(isCalledInEveryPath);
284
- const calledInSomePaths = seenSegments.some(isCalledInSomePath);
254
+ const returnedSegments = codePath.returnedSegments;
255
+ const calledInEveryPaths = returnedSegments.every(isCalledInEveryPath);
256
+ const calledInSomePaths = returnedSegments.some(isCalledInSomePath);
285
257
 
286
258
  if (!calledInEveryPaths) {
287
259
  context.report({
@@ -296,28 +268,38 @@ module.exports = {
296
268
  /**
297
269
  * Initialize information of a given code path segment.
298
270
  * @param {CodePathSegment} segment A code path segment to initialize.
271
+ * @param {CodePathSegment} node Node that starts the segment.
299
272
  * @returns {void}
300
273
  */
301
- onCodePathSegmentStart(segment) {
274
+ onCodePathSegmentStart(segment, node) {
302
275
 
303
276
  funcInfo.currentSegments.add(segment);
304
277
 
305
- if (!(funcInfo && funcInfo.isConstructor && funcInfo.hasExtends)) {
278
+ if (!(funcInfo.isConstructor && funcInfo.hasExtends)) {
306
279
  return;
307
280
  }
308
281
 
309
282
  // Initialize info.
310
283
  const info = segInfoMap[segment.id] = new SegmentInfo();
311
284
 
312
- // When there are previous segments, aggregates these.
313
- const prevSegments = segment.prevSegments;
314
-
315
- if (prevSegments.length > 0) {
316
- const seenPrevSegments = prevSegments.filter(hasSegmentBeenSeen);
285
+ const seenPrevSegments = segment.prevSegments.filter(hasSegmentBeenSeen);
317
286
 
287
+ // When there are previous segments, aggregates these.
288
+ if (seenPrevSegments.length > 0) {
318
289
  info.calledInSomePaths = seenPrevSegments.some(isCalledInSomePath);
319
290
  info.calledInEveryPaths = seenPrevSegments.every(isCalledInEveryPath);
320
291
  }
292
+
293
+ /*
294
+ * ForStatement > *.update segments are a special case as they are created in advance,
295
+ * without seen previous segments. Since they logically don't affect `calledInEveryPaths`
296
+ * calculations, and they can never be a lone previous segment of another one, we'll set
297
+ * their `calledInEveryPaths` to `true` to effectively ignore them in those calculations.
298
+ * .
299
+ */
300
+ if (node.parent && node.parent.type === "ForStatement" && node.parent.update === node) {
301
+ info.calledInEveryPaths = true;
302
+ }
321
303
  },
322
304
 
323
305
  onUnreachableCodePathSegmentStart(segment) {
@@ -343,25 +325,30 @@ module.exports = {
343
325
  * @returns {void}
344
326
  */
345
327
  onCodePathSegmentLoop(fromSegment, toSegment) {
346
- if (!(funcInfo && funcInfo.isConstructor && funcInfo.hasExtends)) {
328
+ if (!(funcInfo.isConstructor && funcInfo.hasExtends)) {
347
329
  return;
348
330
  }
349
331
 
350
- // Update information inside of the loop.
351
- const isRealLoop = toSegment.prevSegments.length >= 2;
352
-
353
332
  funcInfo.codePath.traverseSegments(
354
333
  { first: toSegment, last: fromSegment },
355
- segment => {
356
- const info = segInfoMap[segment.id] ?? new SegmentInfo();
334
+ (segment, controller) => {
335
+ const info = segInfoMap[segment.id];
336
+
337
+ // skip segments after the loop
338
+ if (!info) {
339
+ controller.skip();
340
+ return;
341
+ }
342
+
357
343
  const seenPrevSegments = segment.prevSegments.filter(hasSegmentBeenSeen);
344
+ const calledInSomePreviousPaths = seenPrevSegments.some(isCalledInSomePath);
345
+ const calledInEveryPreviousPaths = seenPrevSegments.every(isCalledInEveryPath);
358
346
 
359
- // Updates flags.
360
- info.calledInSomePaths = seenPrevSegments.some(isCalledInSomePath);
361
- info.calledInEveryPaths = seenPrevSegments.every(isCalledInEveryPath);
347
+ info.calledInSomePaths ||= calledInSomePreviousPaths;
348
+ info.calledInEveryPaths ||= calledInEveryPreviousPaths;
362
349
 
363
350
  // If flags become true anew, reports the valid nodes.
364
- if (info.calledInSomePaths || isRealLoop) {
351
+ if (calledInSomePreviousPaths) {
365
352
  const nodes = info.validNodes;
366
353
 
367
354
  info.validNodes = [];
@@ -375,9 +362,6 @@ module.exports = {
375
362
  });
376
363
  }
377
364
  }
378
-
379
- // save just in case we created a new SegmentInfo object
380
- segInfoMap[segment.id] = info;
381
365
  }
382
366
  );
383
367
  },
@@ -388,7 +372,7 @@ module.exports = {
388
372
  * @returns {void}
389
373
  */
390
374
  "CallExpression:exit"(node) {
391
- if (!(funcInfo && funcInfo.isConstructor)) {
375
+ if (!(funcInfo.isConstructor && funcInfo.hasExtends)) {
392
376
  return;
393
377
  }
394
378
 
@@ -398,41 +382,34 @@ module.exports = {
398
382
  }
399
383
 
400
384
  // Reports if needed.
401
- if (funcInfo.hasExtends) {
402
- const segments = funcInfo.currentSegments;
403
- let duplicate = false;
404
- let info = null;
385
+ const segments = funcInfo.currentSegments;
386
+ let duplicate = false;
387
+ let info = null;
405
388
 
406
- for (const segment of segments) {
389
+ for (const segment of segments) {
407
390
 
408
- if (segment.reachable) {
409
- info = segInfoMap[segment.id];
391
+ if (segment.reachable) {
392
+ info = segInfoMap[segment.id];
410
393
 
411
- duplicate = duplicate || info.calledInSomePaths;
412
- info.calledInSomePaths = info.calledInEveryPaths = true;
413
- }
394
+ duplicate = duplicate || info.calledInSomePaths;
395
+ info.calledInSomePaths = info.calledInEveryPaths = true;
414
396
  }
397
+ }
415
398
 
416
- if (info) {
417
- if (duplicate) {
418
- context.report({
419
- messageId: "duplicate",
420
- node
421
- });
422
- } else if (!funcInfo.superIsConstructor) {
423
- context.report({
424
- messageId: "badSuper",
425
- node
426
- });
427
- } else {
428
- info.validNodes.push(node);
429
- }
399
+ if (info) {
400
+ if (duplicate) {
401
+ context.report({
402
+ messageId: "duplicate",
403
+ node
404
+ });
405
+ } else if (!funcInfo.superIsConstructor) {
406
+ context.report({
407
+ messageId: "badSuper",
408
+ node
409
+ });
410
+ } else {
411
+ info.validNodes.push(node);
430
412
  }
431
- } else if (isAnySegmentReachable(funcInfo.currentSegments)) {
432
- context.report({
433
- messageId: "unexpected",
434
- node
435
- });
436
413
  }
437
414
  },
438
415
 
@@ -442,7 +419,7 @@ module.exports = {
442
419
  * @returns {void}
443
420
  */
444
421
  ReturnStatement(node) {
445
- if (!(funcInfo && funcInfo.isConstructor && funcInfo.hasExtends)) {
422
+ if (!(funcInfo.isConstructor && funcInfo.hasExtends)) {
446
423
  return;
447
424
  }
448
425
 
@@ -462,14 +439,6 @@ module.exports = {
462
439
  info.calledInSomePaths = info.calledInEveryPaths = true;
463
440
  }
464
441
  }
465
- },
466
-
467
- /**
468
- * Resets state.
469
- * @returns {void}
470
- */
471
- "Program:exit"() {
472
- segInfoMap = Object.create(null);
473
442
  }
474
443
  };
475
444
  }
@@ -117,7 +117,7 @@ module.exports = {
117
117
  };
118
118
 
119
119
  ruleDef.VariableDeclaration = function(node) {
120
- if (node.kind === "let" || node.kind === "const") {
120
+ if (node.kind !== "var") {
121
121
  markLoneBlock(node);
122
122
  }
123
123
  };
@@ -15,6 +15,11 @@ const astUtils = require("./utils/ast-utils");
15
15
  // Typedefs
16
16
  //------------------------------------------------------------------------------
17
17
 
18
+ /**
19
+ * A simple name for the types of variables that this rule supports
20
+ * @typedef {'array-destructure'|'catch-clause'|'parameter'|'variable'} VariableType
21
+ */
22
+
18
23
  /**
19
24
  * Bag of data used for formatting the `unusedVar` lint message.
20
25
  * @typedef {Object} UnusedVarMessageData
@@ -23,6 +28,13 @@ const astUtils = require("./utils/ast-utils");
23
28
  * @property {string} additional Any additional info to be appended at the end.
24
29
  */
25
30
 
31
+ /**
32
+ * Bag of data used for formatting the `usedIgnoredVar` lint message.
33
+ * @typedef {Object} UsedIgnoredVarMessageData
34
+ * @property {string} varName The name of the unused var.
35
+ * @property {string} additional Any additional info to be appended at the end.
36
+ */
37
+
26
38
  //------------------------------------------------------------------------------
27
39
  // Rule Definition
28
40
  //------------------------------------------------------------------------------
@@ -73,6 +85,9 @@ module.exports = {
73
85
  },
74
86
  ignoreClassWithStaticInitBlock: {
75
87
  type: "boolean"
88
+ },
89
+ reportUsedIgnorePattern: {
90
+ type: "boolean"
76
91
  }
77
92
  },
78
93
  additionalProperties: false
@@ -82,7 +97,8 @@ module.exports = {
82
97
  ],
83
98
 
84
99
  messages: {
85
- unusedVar: "'{{varName}}' is {{action}} but never used{{additional}}."
100
+ unusedVar: "'{{varName}}' is {{action}} but never used{{additional}}.",
101
+ usedIgnoredVar: "'{{varName}}' is marked as ignored but is used{{additional}}."
86
102
  }
87
103
  },
88
104
 
@@ -96,7 +112,8 @@ module.exports = {
96
112
  args: "after-used",
97
113
  ignoreRestSiblings: false,
98
114
  caughtErrors: "all",
99
- ignoreClassWithStaticInitBlock: false
115
+ ignoreClassWithStaticInitBlock: false,
116
+ reportUsedIgnorePattern: false
100
117
  };
101
118
 
102
119
  const firstOption = context.options[0];
@@ -110,6 +127,7 @@ module.exports = {
110
127
  config.ignoreRestSiblings = firstOption.ignoreRestSiblings || config.ignoreRestSiblings;
111
128
  config.caughtErrors = firstOption.caughtErrors || config.caughtErrors;
112
129
  config.ignoreClassWithStaticInitBlock = firstOption.ignoreClassWithStaticInitBlock || config.ignoreClassWithStaticInitBlock;
130
+ config.reportUsedIgnorePattern = firstOption.reportUsedIgnorePattern || config.reportUsedIgnorePattern;
113
131
 
114
132
  if (firstOption.varsIgnorePattern) {
115
133
  config.varsIgnorePattern = new RegExp(firstOption.varsIgnorePattern, "u");
@@ -129,6 +147,50 @@ module.exports = {
129
147
  }
130
148
  }
131
149
 
150
+ /**
151
+ * Gets a given variable's description and configured ignore pattern
152
+ * based on the provided variableType
153
+ * @param {VariableType} variableType a simple name for the types of variables that this rule supports
154
+ * @throws {Error} (Unreachable)
155
+ * @returns {[string | undefined, string | undefined]} the given variable's description and
156
+ * ignore pattern
157
+ */
158
+ function getVariableDescription(variableType) {
159
+ let pattern;
160
+ let variableDescription;
161
+
162
+ switch (variableType) {
163
+ case "array-destructure":
164
+ pattern = config.destructuredArrayIgnorePattern;
165
+ variableDescription = "elements of array destructuring";
166
+ break;
167
+
168
+ case "catch-clause":
169
+ pattern = config.caughtErrorsIgnorePattern;
170
+ variableDescription = "args";
171
+ break;
172
+
173
+ case "parameter":
174
+ pattern = config.argsIgnorePattern;
175
+ variableDescription = "args";
176
+ break;
177
+
178
+ case "variable":
179
+ pattern = config.varsIgnorePattern;
180
+ variableDescription = "vars";
181
+ break;
182
+
183
+ default:
184
+ throw new Error(`Unexpected variable type: ${variableType}`);
185
+ }
186
+
187
+ if (pattern) {
188
+ pattern = pattern.toString();
189
+ }
190
+
191
+ return [variableDescription, pattern];
192
+ }
193
+
132
194
  /**
133
195
  * Generates the message data about the variable being defined and unused,
134
196
  * including the ignore pattern if configured.
@@ -136,27 +198,42 @@ module.exports = {
136
198
  * @returns {UnusedVarMessageData} The message data to be used with this unused variable.
137
199
  */
138
200
  function getDefinedMessageData(unusedVar) {
139
- const defType = unusedVar.defs && unusedVar.defs[0] && unusedVar.defs[0].type;
140
- let type;
141
- let pattern;
201
+ const def = unusedVar.defs && unusedVar.defs[0];
202
+ let additionalMessageData = "";
142
203
 
143
- if (defType === "CatchClause" && config.caughtErrorsIgnorePattern) {
144
- type = "args";
145
- pattern = config.caughtErrorsIgnorePattern.toString();
146
- } else if (defType === "Parameter" && config.argsIgnorePattern) {
147
- type = "args";
148
- pattern = config.argsIgnorePattern.toString();
149
- } else if (defType !== "Parameter" && defType !== "CatchClause" && config.varsIgnorePattern) {
150
- type = "vars";
151
- pattern = config.varsIgnorePattern.toString();
152
- }
204
+ if (def) {
205
+ let pattern;
206
+ let variableDescription;
207
+
208
+ switch (def.type) {
209
+ case "CatchClause":
210
+ if (config.caughtErrorsIgnorePattern) {
211
+ [variableDescription, pattern] = getVariableDescription("catch-clause");
212
+ }
213
+ break;
153
214
 
154
- const additional = type ? `. Allowed unused ${type} must match ${pattern}` : "";
215
+ case "Parameter":
216
+ if (config.argsIgnorePattern) {
217
+ [variableDescription, pattern] = getVariableDescription("parameter");
218
+ }
219
+ break;
220
+
221
+ default:
222
+ if (config.varsIgnorePattern) {
223
+ [variableDescription, pattern] = getVariableDescription("variable");
224
+ }
225
+ break;
226
+ }
227
+
228
+ if (pattern && variableDescription) {
229
+ additionalMessageData = `. Allowed unused ${variableDescription} must match ${pattern}`;
230
+ }
231
+ }
155
232
 
156
233
  return {
157
234
  varName: unusedVar.name,
158
235
  action: "defined",
159
- additional
236
+ additional: additionalMessageData
160
237
  };
161
238
  }
162
239
 
@@ -167,19 +244,51 @@ module.exports = {
167
244
  * @returns {UnusedVarMessageData} The message data to be used with this unused variable.
168
245
  */
169
246
  function getAssignedMessageData(unusedVar) {
170
- const def = unusedVar.defs[0];
171
- let additional = "";
247
+ const def = unusedVar.defs && unusedVar.defs[0];
248
+ let additionalMessageData = "";
249
+
250
+ if (def) {
251
+ let pattern;
252
+ let variableDescription;
172
253
 
173
- if (config.destructuredArrayIgnorePattern && def && def.name.parent.type === "ArrayPattern") {
174
- additional = `. Allowed unused elements of array destructuring patterns must match ${config.destructuredArrayIgnorePattern.toString()}`;
175
- } else if (config.varsIgnorePattern) {
176
- additional = `. Allowed unused vars must match ${config.varsIgnorePattern.toString()}`;
254
+ if (def.name.parent.type === "ArrayPattern" && config.destructuredArrayIgnorePattern) {
255
+ [variableDescription, pattern] = getVariableDescription("array-destructure");
256
+ } else if (config.varsIgnorePattern) {
257
+ [variableDescription, pattern] = getVariableDescription("variable");
258
+ }
259
+
260
+ if (pattern && variableDescription) {
261
+ additionalMessageData = `. Allowed unused ${variableDescription} must match ${pattern}`;
262
+ }
177
263
  }
178
264
 
179
265
  return {
180
266
  varName: unusedVar.name,
181
267
  action: "assigned a value",
182
- additional
268
+ additional: additionalMessageData
269
+ };
270
+ }
271
+
272
+ /**
273
+ * Generate the warning message about a variable being used even though
274
+ * it is marked as being ignored.
275
+ * @param {Variable} variable eslint-scope variable object
276
+ * @param {VariableType} variableType a simple name for the types of variables that this rule supports
277
+ * @returns {UsedIgnoredVarMessageData} The message data to be used with
278
+ * this used ignored variable.
279
+ */
280
+ function getUsedIgnoredMessageData(variable, variableType) {
281
+ const [variableDescription, pattern] = getVariableDescription(variableType);
282
+
283
+ let additionalMessageData = "";
284
+
285
+ if (pattern && variableDescription) {
286
+ additionalMessageData = `. Used ${variableDescription} must not match ${pattern}`;
287
+ }
288
+
289
+ return {
290
+ varName: variable.name,
291
+ additional: additionalMessageData
183
292
  };
184
293
  }
185
294
 
@@ -532,8 +641,13 @@ module.exports = {
532
641
  * @private
533
642
  */
534
643
  function isUsedVariable(variable) {
535
- const functionNodes = getFunctionDefinitions(variable),
536
- isFunctionDefinition = functionNodes.length > 0;
644
+ if (variable.eslintUsed) {
645
+ return true;
646
+ }
647
+
648
+ const functionNodes = getFunctionDefinitions(variable);
649
+ const isFunctionDefinition = functionNodes.length > 0;
650
+
537
651
  let rhsNode = null;
538
652
 
539
653
  return variable.references.some(ref => {
@@ -589,8 +703,13 @@ module.exports = {
589
703
  continue;
590
704
  }
591
705
 
592
- // skip function expression names and variables marked with markVariableAsUsed()
593
- if (scope.functionExpressionScope || variable.eslintUsed) {
706
+ // skip function expression names
707
+ if (scope.functionExpressionScope) {
708
+ continue;
709
+ }
710
+
711
+ // skip variables marked with markVariableAsUsed()
712
+ if (!config.reportUsedIgnorePattern && variable.eslintUsed) {
594
713
  continue;
595
714
  }
596
715
 
@@ -615,6 +734,14 @@ module.exports = {
615
734
  config.destructuredArrayIgnorePattern &&
616
735
  config.destructuredArrayIgnorePattern.test(def.name.name)
617
736
  ) {
737
+ if (config.reportUsedIgnorePattern && isUsedVariable(variable)) {
738
+ context.report({
739
+ node: def.name,
740
+ messageId: "usedIgnoredVar",
741
+ data: getUsedIgnoredMessageData(variable, "array-destructure")
742
+ });
743
+ }
744
+
618
745
  continue;
619
746
  }
620
747
 
@@ -634,6 +761,14 @@ module.exports = {
634
761
 
635
762
  // skip ignored parameters
636
763
  if (config.caughtErrorsIgnorePattern && config.caughtErrorsIgnorePattern.test(def.name.name)) {
764
+ if (config.reportUsedIgnorePattern && isUsedVariable(variable)) {
765
+ context.report({
766
+ node: def.name,
767
+ messageId: "usedIgnoredVar",
768
+ data: getUsedIgnoredMessageData(variable, "catch-clause")
769
+ });
770
+ }
771
+
637
772
  continue;
638
773
  }
639
774
  } else if (type === "Parameter") {
@@ -650,6 +785,14 @@ module.exports = {
650
785
 
651
786
  // skip ignored parameters
652
787
  if (config.argsIgnorePattern && config.argsIgnorePattern.test(def.name.name)) {
788
+ if (config.reportUsedIgnorePattern && isUsedVariable(variable)) {
789
+ context.report({
790
+ node: def.name,
791
+ messageId: "usedIgnoredVar",
792
+ data: getUsedIgnoredMessageData(variable, "parameter")
793
+ });
794
+ }
795
+
653
796
  continue;
654
797
  }
655
798
 
@@ -661,6 +804,14 @@ module.exports = {
661
804
 
662
805
  // skip ignored variables
663
806
  if (config.varsIgnorePattern && config.varsIgnorePattern.test(def.name.name)) {
807
+ if (config.reportUsedIgnorePattern && isUsedVariable(variable)) {
808
+ context.report({
809
+ node: def.name,
810
+ messageId: "usedIgnoredVar",
811
+ data: getUsedIgnoredMessageData(variable, "variable")
812
+ });
813
+ }
814
+
664
815
  continue;
665
816
  }
666
817
  }
@@ -724,6 +875,5 @@ module.exports = {
724
875
  }
725
876
  }
726
877
  };
727
-
728
878
  }
729
879
  };
@@ -182,7 +182,7 @@ module.exports = {
182
182
 
183
183
  if (
184
184
  (methodName === "indexOf" || methodName === "lastIndexOf") &&
185
- node.arguments.length === 1 &&
185
+ node.arguments.length <= 2 &&
186
186
  isNaNIdentifier(node.arguments[0])
187
187
  ) {
188
188
 
@@ -190,7 +190,7 @@ module.exports = {
190
190
  * To retain side effects, it's essential to address `NaN` beforehand, which
191
191
  * is not possible with fixes like `arr.findIndex(Number.isNaN)`.
192
192
  */
193
- const isSuggestable = node.arguments[0].type !== "SequenceExpression";
193
+ const isSuggestable = node.arguments[0].type !== "SequenceExpression" && !node.arguments[1];
194
194
  const suggestedFixes = [];
195
195
 
196
196
  if (isSuggestable) {
@@ -0,0 +1,30 @@
1
+ /**
2
+ * @fileoverview Provides helper functions to start/stop the time measurements
3
+ * that are provided by the ESLint 'stats' option.
4
+ * @author Mara Kiefer <http://github.com/mnkiefer>
5
+ */
6
+ "use strict";
7
+
8
+ /**
9
+ * Start time measurement
10
+ * @returns {[number, number]} t variable for tracking time
11
+ */
12
+ function startTime() {
13
+ return process.hrtime();
14
+ }
15
+
16
+ /**
17
+ * End time measurement
18
+ * @param {[number, number]} t Variable for tracking time
19
+ * @returns {number} The measured time in milliseconds
20
+ */
21
+ function endTime(t) {
22
+ const time = process.hrtime(t);
23
+
24
+ return time[0] * 1e3 + time[1] / 1e6;
25
+ }
26
+
27
+ module.exports = {
28
+ startTime,
29
+ endTime
30
+ };