@jslint-org/jslint 2021.12.20 → 2022.5.20

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/jslint.mjs CHANGED
@@ -1,14 +1,15 @@
1
1
  // #!/usr/bin/env node
2
2
  // JSLint
3
- // Original Author: Douglas Crockford (https://www.jslint.com).
4
3
 
4
+ // The Unlicense
5
+ //
5
6
  // This is free and unencumbered software released into the public domain.
6
-
7
+ //
7
8
  // Anyone is free to copy, modify, publish, use, compile, sell, or
8
9
  // distribute this software, either in source code form or as a compiled
9
10
  // binary, for any purpose, commercial or non-commercial, and by any
10
11
  // means.
11
-
12
+ //
12
13
  // In jurisdictions that recognize copyright laws, the author or authors
13
14
  // of this software dedicate any and all copyright interest in the
14
15
  // software to the public domain. We make this dedication for the benefit
@@ -16,7 +17,7 @@
16
17
  // successors. We intend this dedication to be an overt act of
17
18
  // relinquishment in perpetuity of all present and future rights to this
18
19
  // software under copyright law.
19
-
20
+ //
20
21
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
22
  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
23
  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
@@ -24,7 +25,7 @@
24
25
  // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
25
26
  // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
26
27
  // OTHER DEALINGS IN THE SOFTWARE.
27
-
28
+ //
28
29
  // For more information, please refer to <https://unlicense.org/>
29
30
 
30
31
 
@@ -92,50 +93,52 @@
92
93
  // WARNING: JSLint will hurt your feelings.
93
94
 
94
95
  /*jslint beta, node*/
95
-
96
96
  /*property
97
- mode_conditional,
97
+ excludeList,
98
+ globExclude,
99
+ import_meta_url, includeList,
100
+ pathnameList,
98
101
  JSLINT_BETA, NODE_V8_COVERAGE, a, all, argv, arity, artifact,
99
102
  assertErrorThrownAsync, assertJsonEqual, assertOrThrow, assign, async, b,
100
103
  beta, bitwise, block, body, browser, c, calls, catch, catch_list,
101
- catch_stack, causes, char, children, clear, closer,
102
- closure, code, column, concat, consoleError, console_error, console_log,
103
- constant, context, convert, count, coverageDir, create, cwd, d, dead,
104
- debugInline, default, delta, devel, directive, directive_list,
105
- directive_quiet, directives, dirname, disrupt, dot, edition, elem_list,
106
- ellipsis, else, end, endOffset, endsWith, entries, env, error, eval, every,
107
- example_list, exec, execArgv, exit, export_dict, exports, expression, extra,
108
- file, fileList, fileURLToPath, filter, finally, flag, floor, for, forEach,
109
- formatted_message, free, freeze, from, froms,
110
- fsWriteFileWithParents, fud, functionName, function_list, function_stack,
111
- functions, get, getset, github_repo, global, global_dict, global_list,
112
- holeList, htmlEscape, id, identifier, import, import_list, inc, indent2,
113
- index, indexOf, init, initial, isArray, isBlockCoverage, isHole, isNaN,
114
- is_equal, is_weird, join, jslint, jslint_apidoc, jslint_assert,
115
- jslint_charset_ascii, jslint_cli, jslint_edition, jslint_phase1_split,
116
- jslint_phase2_lex, jslint_phase3_parse, jslint_phase4_walk,
117
- jslint_phase5_whitage, jslint_report, json, jstestDescribe, jstestIt,
118
- jstestOnExit, keys, label, lbp, led, length, level, line, lineList,
119
- line_list, line_offset, line_source, lines, linesCovered, linesTotal, live,
120
- log, long, loop, m, main, map, margin, match, max, message, meta, min,
121
- mkdir, modeCoverageIgnoreFile, modeIndex, mode_cli, mode_json, mode_module,
122
- mode_noop, mode_property, mode_shebang, mode_stop, module, moduleFsInit,
123
- moduleName, module_list, name, names, node, noop, now,
124
- nr, nud, objectDeepCopyWithKeysSorted, ok, on, open, opening, option,
125
- option_dict, order, package_name, padEnd, padStart, parameters, parent,
126
- parentIi, parse, pathname, platform, pop, processArgv, process_argv,
127
- process_env, process_exit, process_version, promises, property,
128
- property_dict, push, quote, ranges, readFile, readdir, readonly, recursive,
129
- reduce, repeat, replace, resolve, result, reverse, rm, rmdir, role, round,
130
- scriptId, search, set, shebang, shift, signature, single, slice, some, sort,
131
- source, spawn, splice, split, stack, stack_trace, start, startOffset,
132
- startsWith, statement, statement_prv, stdio, stop, stop_at, stringify,
133
- switch, syntax_dict, tenure, test, test_cause, test_internal_error, this,
134
- thru, toString, token, token_global, token_list, token_nxt, token_tree,
135
- tokens, trace, tree, trim, trimEnd, trimRight, try, type, unlink, unordered,
136
- unshift, url, used, v8CoverageListMerge, v8CoverageReportCreate, value,
137
- variable, version, versions, warn, warn_at, warning, warning_list, warnings,
138
- white, wrapped, writeFile
104
+ catch_stack, causes, char, children, clear, closer, closure, code, column,
105
+ concat, consoleError, console_error, console_log, constant, context,
106
+ convert, count, coverageDir, create, cwd, d, dead, debugInline, default,
107
+ delta, devel, directive, directive_list, directive_quiet, directives,
108
+ dirname, disrupt, dot, edition, elem_list, ellipsis, else, end, endOffset,
109
+ endsWith, entries, env, error, eval, every, example_list, exec, execArgv,
110
+ exit, exitCode, export_dict, exports, expression, extra, file, fileList,
111
+ fileURLToPath, filter, finally, flag, floor, for, forEach,
112
+ formatted_message, free, freeze, from, froms, fsWriteFileWithParents,
113
+ fud_stmt, functionName, function_list, function_stack, functions, get,
114
+ getset, github_repo, global, global_dict, global_list, holeList, htmlEscape,
115
+ id, identifier, import, import_list, inc, indent2, index, indexOf, init,
116
+ initial, isArray, isBlockCoverage, isHole, isNaN, is_equal, is_fart,
117
+ is_weird, join, jslint, jslint_apidoc, jslint_assert, jslint_charset_ascii,
118
+ jslint_cli, jslint_edition, jslint_phase1_split, jslint_phase2_lex,
119
+ jslint_phase3_parse, jslint_phase4_walk, jslint_phase5_whitage,
120
+ jslint_report, json, jstestDescribe, jstestIt, jstestOnExit, keys, label,
121
+ lbp, led_infix, length, level, line, lineList, line_list, line_offset,
122
+ line_source, lines, linesCovered, linesTotal, live, log, long, loop, m, map,
123
+ margin, match, max, message, meta, min, mkdir, modeCoverageIgnoreFile,
124
+ modeIndex, mode_cli, mode_conditional, mode_json, mode_module, mode_noop,
125
+ mode_property, mode_shebang, mode_stop, module, moduleFsInit, moduleName,
126
+ module_list, name, names, node, noop, now, nr, nud_prefix,
127
+ objectDeepCopyWithKeysSorted, ok, on, open, opening, option, option_dict,
128
+ order, package_name, padEnd, padStart, parameters, parent, parentIi, parse,
129
+ pathname, platform, pop, processArgv, process_argv, process_env,
130
+ process_exit, promises, property, property_dict, push, quote, ranges,
131
+ readFile, readdir, readonly, recursive, reduce, repeat, replace, resolve,
132
+ result, reverse, role, round, scriptId, search, set, shebang, shift,
133
+ signature, single, slice, some, sort, source, spawn, splice, split, stack,
134
+ stack_trace, start, startOffset, startsWith, statement, statement_prv,
135
+ stdio, stop, stop_at, stringify, switch, syntax_dict, tenure, test,
136
+ test_cause, test_internal_error, this, thru, toString, token, token_global,
137
+ token_list, token_nxt, token_tree, tokens, trace, tree, trim, trimEnd,
138
+ trimRight, try, type, unlink, unordered, unshift, url, used,
139
+ v8CoverageListMerge, v8CoverageReportCreate, value, variable, version,
140
+ versions, warn, warn_at, warning, warning_list, warnings, white, wrapped,
141
+ writeFile
139
142
  */
140
143
 
141
144
  // init debugInline
@@ -165,11 +168,99 @@ let jslint_charset_ascii = (
165
168
  + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
166
169
  + "`abcdefghijklmnopqrstuvwxyz{|}~\u007f"
167
170
  );
168
- let jslint_edition = "v2021.12.20";
171
+ let jslint_edition = "v2022.5.20";
169
172
  let jslint_export; // The jslint object to be exported.
170
173
  let jslint_fudge = 1; // Fudge starting line and starting
171
174
  // ... column to 1.
172
175
  let jslint_import_meta_url = ""; // import.meta.url used by cli.
176
+ let jslint_rgx_cap = (
177
+ /^[A-Z]/
178
+ );
179
+ let jslint_rgx_crlf = (
180
+ /\n|\r\n?/
181
+ );
182
+ let jslint_rgx_digits_bits = (
183
+ /^[01_]*/
184
+ );
185
+ let jslint_rgx_digits_decimals = (
186
+ /^[0-9_]*/
187
+ );
188
+ let jslint_rgx_digits_hexs = (
189
+ /^[0-9A-F_]*/i
190
+ );
191
+ let jslint_rgx_digits_octals = (
192
+ /^[0-7_]*/
193
+ );
194
+ let jslint_rgx_directive = (
195
+ /^(jslint|property|global)\s+(.*)$/
196
+ );
197
+ let jslint_rgx_directive_part = (
198
+ /([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*|$/g
199
+ );
200
+ let jslint_rgx_identifier = (
201
+ /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/
202
+ );
203
+ let jslint_rgx_json_number = (
204
+
205
+ // https://datatracker.ietf.org/doc/html/rfc7159#section-6
206
+ // number = [ minus ] int [ frac ] [ exp ]
207
+
208
+ /^-?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][\-+]?\d+)?$/
209
+ );
210
+ let jslint_rgx_mega = (
211
+
212
+ // Vim-hack - vim-editor has trouble parsing naked '`' in regexp
213
+
214
+ /[\u0060\\]|\$\{/
215
+ );
216
+ let jslint_rgx_module = (
217
+ /^[a-zA-Z0-9_$:.@\-\/]+$/
218
+ );
219
+ let jslint_rgx_numeric_separator_illegal = (
220
+ /__|_$|_n$/m
221
+ );
222
+ let jslint_rgx_slash_star_or_slash = (
223
+ /\/\*|\/$/
224
+ );
225
+ let jslint_rgx_tab = (
226
+ /\t/g
227
+ );
228
+ let jslint_rgx_todo = (
229
+ /\b(?:todo|TO\s?DO|HACK)\b/
230
+ );
231
+ let jslint_rgx_token = new RegExp(
232
+ "^("
233
+ + "(\\s+)"
234
+ + "|([a-zA-Z_$][a-zA-Z0-9_$]*)"
235
+ + "|[(){}\\[\\],:;'\"~\\`]"
236
+ + "|\\?[?.]?"
237
+ + "|=(?:==?|>)?"
238
+ + "|\\.+"
239
+ + "|\\*[*\\/=]?"
240
+ + "|\\/[*\\/]?"
241
+ + "|\\+[=+]?"
242
+ + "|-[=\\-]?"
243
+ + "|[\\^%]=?"
244
+ + "|&[&=]?"
245
+ + "|\\"
246
+ + "|[|=]?"
247
+ + "|>{1,3}=?"
248
+ + "|<<?=?"
249
+ + "|!(?:!|==?)?"
250
+
251
+ // PR-351 - Add BigInt support.
252
+ // PR-390 - Add numeric-separator support.
253
+
254
+ + "|((?:0_?|[1-9][0-9_]*)n?)"
255
+ + ")"
256
+ + "(.*)$"
257
+ );
258
+ let jslint_rgx_url_search_window_jslint = (
259
+ /[&?]window_jslint=1(?:$|&)/m
260
+ );
261
+ let jslint_rgx_weird_property = (
262
+ /^_|\$|Sync$|_$/m
263
+ );
173
264
  let jstestCountFailed = 0;
174
265
  let jstestCountTotal = 0;
175
266
  let jstestItCount = 0;
@@ -265,6 +356,224 @@ async function fsWriteFileWithParents(pathname, data) {
265
356
  console.error("wrote file " + pathname);
266
357
  }
267
358
 
359
+ function globExclude({
360
+ excludeList = [],
361
+ includeList = [],
362
+ pathnameList = []
363
+ }) {
364
+
365
+ // This function will
366
+ // 1. Exclude pathnames in <pathnameList> that don't match glob-patterns in
367
+ // <includeList>.
368
+ // 2. Exclude pathnames in <pathnameList> that match glob-patterns in
369
+ // <excludeList>.
370
+
371
+ function globAssertNotWeird(list, name) {
372
+
373
+ // This function will check if <list> of strings contain weird characters.
374
+
375
+ [
376
+ [
377
+ "\n", (
378
+ /^.*?([\u0000-\u0007\r]).*/gm
379
+ )
380
+ ],
381
+ [
382
+ "\r", (
383
+ /^.*?([\n]).*/gm
384
+ )
385
+ ]
386
+ ].forEach(function ([
387
+ separator, rgx
388
+ ]) {
389
+ list.join(separator).replace(rgx, function (match0, char) {
390
+ throw new Error(
391
+ "Weird character "
392
+ + JSON.stringify(char)
393
+ + " found in " + name + " "
394
+ + JSON.stringify(match0)
395
+ );
396
+ });
397
+ });
398
+ }
399
+
400
+ function globToRegexp(pattern) {
401
+
402
+ // This function will translate glob <pattern> to javascript-regexp,
403
+ // which javascript can then use to "glob" pathnames.
404
+
405
+ let ii = 0;
406
+ let isClass = false;
407
+ let strClass = "";
408
+ let strRegex = "";
409
+ pattern = pattern.replace((
410
+ /\/\/+/g
411
+ ), "/");
412
+ pattern = pattern.replace((
413
+ /\*\*\*+/g
414
+ ), "**");
415
+ pattern.replace((
416
+ /\\\\|\\\[|\\\]|\[|\]|./g
417
+ ), function (match0) {
418
+ switch (match0) {
419
+ case "[":
420
+ if (isClass) {
421
+ strClass += "[";
422
+ return;
423
+ }
424
+ strClass += "\u0000";
425
+ strRegex += "\u0000";
426
+ isClass = true;
427
+ return;
428
+ case "]":
429
+ if (isClass) {
430
+ isClass = false;
431
+ return;
432
+ }
433
+ strRegex += "]";
434
+ return;
435
+ default:
436
+ if (isClass) {
437
+ strClass += match0;
438
+ return;
439
+ }
440
+ strRegex += match0;
441
+ }
442
+ return "";
443
+ });
444
+ strClass += "\u0000";
445
+
446
+ // An expression "[!...]" matches a single character, namely any character that
447
+ // is not matched by the expression obtained by removing the first '!' from it.
448
+ // (Thus, "[!a-]" matches any single character except 'a', and '-'.)
449
+
450
+ strClass = strClass.replace((
451
+ /\u0000!/g
452
+ ), "\u0000^");
453
+
454
+ // One may include '-' in its literal meaning by making it the first or last
455
+ // character between the brackets.
456
+
457
+ strClass = strClass.replace((
458
+ /\u0000-/g
459
+ ), "\u0000\\-");
460
+ strClass = strClass.replace((
461
+ /-\u0000/g
462
+ ), "\\-\u0000");
463
+
464
+ // Escape brackets '[', ']' in character class.
465
+
466
+ strClass = strClass.replace((
467
+ /[\[\]]/g
468
+ ), "\\$&");
469
+
470
+ // https://stackoverflow.com/questions/3561493
471
+ // /is-there-a-regexp-escape-function-in-javascript
472
+ // $()*+-./?[\]^{|}
473
+
474
+ strRegex = strRegex.replace((
475
+ // ignore [-/]
476
+ /[$()*+.?\[\\\]\^{|}]/g
477
+ ), "\\$&");
478
+
479
+ // Expand wildcard '**/*'.
480
+
481
+ strRegex = strRegex.replace((
482
+ /\\\*\\\*\/(?:\\\*)+/g
483
+ ), ".*?");
484
+
485
+ // Expand wildcard '**'.
486
+
487
+ strRegex = strRegex.replace((
488
+ /(^|\/)\\\*\\\*(\/|$)/gm
489
+ ), "$1.*?$2");
490
+
491
+ // Expand wildcard '*'.
492
+
493
+ strRegex = strRegex.replace((
494
+ /(?:\\\*)+/g
495
+ ), "[^\\/]*?");
496
+
497
+ // Expand wildcard '?'.
498
+
499
+ strRegex = strRegex.replace((
500
+ /\\\?/g
501
+ ), "[^\\/]");
502
+
503
+ // Expand directory-with-trailing-slash '.../'.
504
+
505
+ strRegex = strRegex.replace((
506
+ /\/$/gm
507
+ ), "\\/.*?");
508
+
509
+ // Merge strClass into strRegex.
510
+
511
+ ii = 0;
512
+ strClass = strClass.split("\u0000");
513
+ strRegex = strRegex.replace((
514
+ /\u0000/g
515
+ ), function () {
516
+ ii += 1;
517
+ if (strClass[ii] === "") {
518
+ return "";
519
+ }
520
+ return "[" + strClass[ii] + "]";
521
+ });
522
+
523
+ // Change strRegex from string to regexp.
524
+
525
+ strRegex = new RegExp("^" + strRegex + "$", "gm");
526
+ return strRegex;
527
+ }
528
+
529
+ // Validate excludeList, includeList, pathnameList.
530
+
531
+ globAssertNotWeird(excludeList, "pattern");
532
+ globAssertNotWeird(includeList, "pattern");
533
+ globAssertNotWeird(pathnameList, "pathname");
534
+
535
+ // Optimization
536
+ // Concat pathnames into a single, newline-separated string,
537
+ // whose pathnames can all be filtered with a single, regexp-pass.
538
+
539
+ pathnameList = pathnameList.join("\n");
540
+
541
+ // 1. Exclude pathnames in <pathnameList> that don't match glob-patterns in
542
+ // <includeList>.
543
+
544
+ if (includeList.length > 0) {
545
+ includeList = includeList.map(globToRegexp);
546
+ includeList.forEach(function (pattern) {
547
+ pathnameList = pathnameList.replace(pattern, "\u0000$&");
548
+ });
549
+ pathnameList = pathnameList.replace((
550
+ /^[^\u0000].*/gm
551
+ ), "");
552
+ pathnameList = pathnameList.replace((
553
+ /^\u0000+/gm
554
+ ), "");
555
+ }
556
+
557
+ // 2. Exclude pathnames in <pathnameList> that match glob-patterns in
558
+ // <excludeList>.
559
+
560
+ excludeList = excludeList.map(globToRegexp);
561
+ excludeList.forEach(function (pattern) {
562
+ pathnameList = pathnameList.replace(pattern, "");
563
+ });
564
+
565
+ // Split newline-separated pathnames back to list.
566
+
567
+ pathnameList = pathnameList.split("\n").filter(function (elem) {
568
+ return elem;
569
+ });
570
+ return {
571
+ excludeList,
572
+ includeList,
573
+ pathnameList
574
+ };
575
+ }
576
+
268
577
  function htmlEscape(str) {
269
578
 
270
579
  // This function will make <str> html-safe by escaping & < >.
@@ -305,10 +614,7 @@ function jslint(
305
614
  let import_list = []; // The array collecting all import-from strings.
306
615
  let line_list = String( // The array containing source lines.
307
616
  "\n" + source
308
- ).split(
309
- // rx_crlf
310
- /\n|\r\n?/
311
- ).map(function (line_source) {
617
+ ).split(jslint_rgx_crlf).map(function (line_source) {
312
618
  return {
313
619
  line_source
314
620
  };
@@ -353,8 +659,6 @@ function jslint(
353
659
  }
354
660
 
355
661
  function is_equal(aa, bb) {
356
- let aa_value;
357
- let bb_value;
358
662
 
359
663
  // test_cause:
360
664
  // ["0&&0", "is_equal", "", "", 0]
@@ -375,6 +679,7 @@ function jslint(
375
679
 
376
680
  // test_cause:
377
681
  // ["`${0}`&&`${0}`", "is_equal", "recurse_isArray", "", 0]
682
+ // ["`${0}`&&`${1}`", "is_equal", "recurse_isArray", "", 0]
378
683
 
379
684
  test_cause("recurse_isArray");
380
685
  return is_equal(value, bb[index]);
@@ -388,21 +693,19 @@ function jslint(
388
693
  // }
389
694
 
390
695
  jslint_assert(!Array.isArray(bb), `Expected !Array.isArray(bb).`);
391
- if (aa.id === "(number)" && bb.id === "(number)") {
696
+ switch (aa.id === bb.id && aa.id) {
697
+ case "(number)":
698
+ case "(string)":
392
699
  return aa.value === bb.value;
393
- }
394
- if (aa.id === "(string)") {
395
- aa_value = aa.value;
396
- } else if (aa.id === "`" && aa.constant) {
397
- aa_value = aa.value[0];
398
- }
399
- if (bb.id === "(string)") {
400
- bb_value = bb.value;
401
- } else if (bb.id === "`" && bb.constant) {
402
- bb_value = bb.value[0];
403
- }
404
- if (typeof aa_value === "string") {
405
- return aa_value === bb_value;
700
+
701
+ // PR-394 - Bugfix
702
+ // Fix jslint falsely believing megastring literals `0` and `1` are similar.
703
+
704
+ case "`":
705
+ if (!is_equal(aa.value, bb.value)) {
706
+ return false;
707
+ }
708
+ break;
406
709
  }
407
710
  if (is_weird(aa) || is_weird(bb)) {
408
711
 
@@ -685,6 +988,12 @@ function jslint(
685
988
  // case "function_in_loop":
686
989
  // mm = `Don't create functions within a loop.`;
687
990
  // break;
991
+
992
+ // PR-390 - Add numeric-separator check.
993
+
994
+ case "illegal_num_separator":
995
+ mm = `Illegal numeric separator '_' at column ${column}.`;
996
+ break;
688
997
  case "infix_in":
689
998
  mm = (
690
999
  `Unexpected 'in'. Compare with undefined,`
@@ -837,6 +1146,15 @@ function jslint(
837
1146
  case "use_double":
838
1147
  mm = `Use double quotes, not single quotes.`;
839
1148
  break;
1149
+
1150
+ // PR-386 - Fix issue #382 - Make fart-related warnings more readable.
1151
+
1152
+ case "use_function_not_fart":
1153
+ mm = (
1154
+ `Use 'function (...)', not '(...) =>' when arrow functions`
1155
+ + ` become too complex.`
1156
+ );
1157
+ break;
840
1158
  case "use_open":
841
1159
  mm = (
842
1160
  `Wrap a ternary expression in parens,`
@@ -870,6 +1188,12 @@ function jslint(
870
1188
  case "wrap_condition":
871
1189
  mm = `Wrap the condition in parens.`;
872
1190
  break;
1191
+
1192
+ // PR-386 - Fix issue #382 - Make fart-related warnings more readable.
1193
+
1194
+ case "wrap_fart_parameter":
1195
+ mm = `Wrap the parameter before '=>' in parens.`;
1196
+ break;
873
1197
  case "wrap_immediate":
874
1198
  mm = (
875
1199
  `Wrap an immediate function invocation in parentheses to assist`
@@ -877,9 +1201,6 @@ function jslint(
877
1201
  + ` result of a function, and not the function itself.`
878
1202
  );
879
1203
  break;
880
- case "wrap_parameter":
881
- mm = `Wrap the parameter in parens.`;
882
- break;
883
1204
  case "wrap_regexp":
884
1205
  mm = `Wrap this regexp in parens to avoid confusion.`;
885
1206
  break;
@@ -1441,6 +1762,7 @@ async function jslint_cli({
1441
1762
  console_error,
1442
1763
  console_log,
1443
1764
  file,
1765
+ import_meta_url,
1444
1766
  mode_cli,
1445
1767
  mode_noop,
1446
1768
  option,
@@ -1455,8 +1777,8 @@ async function jslint_cli({
1455
1777
  let command;
1456
1778
  let data;
1457
1779
  let exit_code = 0;
1458
- let mode_plugin_vim;
1459
1780
  let mode_report;
1781
+ let mode_wrapper_vim;
1460
1782
  let result;
1461
1783
 
1462
1784
  function jslint_from_file({
@@ -1529,7 +1851,7 @@ async function jslint_cli({
1529
1851
  if (result_from_file.warnings.length > 0) {
1530
1852
  exit_code = 1;
1531
1853
  console_error(
1532
- mode_plugin_vim
1854
+ mode_wrapper_vim
1533
1855
 
1534
1856
  // PR-349 - Print warnings in format readable by vim.
1535
1857
 
@@ -1609,11 +1931,26 @@ async function jslint_cli({
1609
1931
  return count;
1610
1932
  }
1611
1933
 
1934
+ // PR-396 - window.jslint
1935
+ // Check import.meta.url for directive to export jslint to window-object.
1936
+ // Useful for ES5-era browser-scripts that rely on window.jslint,
1937
+ // like CodeMirror.
1938
+ //
1939
+ // Example usage:
1940
+ // <script type="module" src="./jslint.mjs?window_jslint=1"></script>
1941
+
1942
+ import_meta_url = import_meta_url || jslint_import_meta_url;
1943
+ if (
1944
+ jslint_rgx_url_search_window_jslint.test(import_meta_url)
1945
+ && (typeof globalThis === "object" && globalThis)
1946
+ ) {
1947
+ globalThis.jslint = jslint;
1948
+ }
1949
+
1612
1950
  // Feature-detect nodejs.
1613
1951
 
1614
1952
  if (!(
1615
- typeof process === "object"
1616
- && process
1953
+ (typeof process === "object" && process)
1617
1954
  && process.versions
1618
1955
  && typeof process.versions.node === "string"
1619
1956
  && !mode_noop
@@ -1639,7 +1976,7 @@ async function jslint_cli({
1639
1976
  ).test(process_argv[1])
1640
1977
  || mode_cli
1641
1978
  )
1642
- && moduleUrl.fileURLToPath(jslint_import_meta_url)
1979
+ && moduleUrl.fileURLToPath(import_meta_url)
1643
1980
  === modulePath.resolve(process_argv[1])
1644
1981
  )
1645
1982
  && !mode_cli
@@ -1662,17 +1999,17 @@ async function jslint_cli({
1662
1999
  }));
1663
2000
  return;
1664
2001
 
1665
- // COMMIT-b26d6df2 - Add command jslint_plugin_vim.
2002
+ // PR-363 - Add command jslint_report.
1666
2003
 
1667
- case "jslint_plugin_vim":
1668
- mode_plugin_vim = true;
2004
+ case "jslint_report":
2005
+ mode_report = command[1];
1669
2006
  process_argv = process_argv.slice(1);
1670
2007
  break;
1671
2008
 
1672
- // PR-363 - Add command jslint_report.
2009
+ // COMMIT-b26d6df2 - Add command jslint_wrapper_vim.
1673
2010
 
1674
- case "jslint_report":
1675
- mode_report = command[1];
2011
+ case "jslint_wrapper_vim":
2012
+ mode_wrapper_vim = true;
1676
2013
  process_argv = process_argv.slice(1);
1677
2014
  break;
1678
2015
 
@@ -1680,6 +2017,7 @@ async function jslint_cli({
1680
2017
 
1681
2018
  case "v8_coverage_report":
1682
2019
  await v8CoverageReportCreate({
2020
+ consoleError: console_error,
1683
2021
  coverageDir: command[1],
1684
2022
  processArgv: process_argv.slice(3)
1685
2023
  });
@@ -1688,9 +2026,9 @@ async function jslint_cli({
1688
2026
 
1689
2027
  // PR-349 - Detect cli-option --mode-vim-plugin.
1690
2028
 
1691
- mode_plugin_vim = (
2029
+ mode_wrapper_vim = (
1692
2030
  process_argv.slice(2).indexOf("--mode-vim-plugin") >= 0
1693
- || mode_plugin_vim
2031
+ || mode_wrapper_vim
1694
2032
  );
1695
2033
 
1696
2034
  // Normalize file relative to process.cwd().
@@ -1784,7 +2122,9 @@ async function jslint_cli({
1784
2122
  option
1785
2123
  });
1786
2124
  if (mode_report) {
1787
- await fsWriteFileWithParents(mode_report, jslint_report(result));
2125
+ result = jslint.jslint_report(result);
2126
+ result = `<body class="JSLINT_ JSLINT_REPORT_">\n${result}</body>\n`;
2127
+ await fsWriteFileWithParents(mode_report, result);
1788
2128
  }
1789
2129
  process_exit(exit_code);
1790
2130
  return exit_code;
@@ -1826,37 +2166,16 @@ function jslint_phase2_lex(state) {
1826
2166
  let line_mega; // The starting line of megastring.
1827
2167
  let line_source = ""; // The remaining line source string.
1828
2168
  let line_whole = ""; // The whole line source string.
2169
+ let mode_digits_empty_string = 1;
2170
+ let mode_digits_numeric_separator = 2;
1829
2171
  let mode_directive = true; // true if directives are still allowed.
1830
2172
  let mode_mega = false; // true if currently parsing a megastring
1831
2173
  // ... literal.
1832
2174
  let mode_regexp; // true if regular expression literal seen on
1833
2175
  // ... this line.
1834
- let rx_token = new RegExp(
1835
- "^("
1836
- + "(\\s+)"
1837
- + "|([a-zA-Z_$][a-zA-Z0-9_$]*)"
1838
- + "|[(){}\\[\\],:;'\"~\\`]"
1839
- + "|\\?[?.]?"
1840
- + "|=(?:==?|>)?"
1841
- + "|\\.+"
1842
- + "|\\*[*\\/=]?"
1843
- + "|\\/[*\\/]?"
1844
- + "|\\+[=+]?"
1845
- + "|-[=\\-]?"
1846
- + "|[\\^%]=?"
1847
- + "|&[&=]?"
1848
- + "|\\"
1849
- + "|[|=]?"
1850
- + "|>{1,3}=?"
1851
- + "|<<?=?"
1852
- + "|!(?:!|==?)?"
1853
-
1854
- // PR-351 - Add BigInt support.
1855
-
1856
- + "|(0n?|[1-9][0-9]*n?)"
1857
- + ")"
1858
- + "(.*)$"
1859
- );
2176
+ let paren_backtrack_list = []; // List of most recent "(" tokens at any
2177
+ // ... paren-depth.
2178
+ let paren_depth = 0; // Keeps track of current paren-depth.
1860
2179
  let snippet = ""; // A piece of string.
1861
2180
  let token_1; // The first token.
1862
2181
  let token_prv = token_global; // The previous token including
@@ -1938,7 +2257,7 @@ function jslint_phase2_lex(state) {
1938
2257
 
1939
2258
  warn_at("unexpected_a", line, column, char);
1940
2259
  }
1941
- if (read_digits("x") > 5) {
2260
+ if (read_digits("x", undefined) > 5) {
1942
2261
 
1943
2262
  // test_cause:
1944
2263
  // ["\"\\u{123456}\"", "char_after_escape", "too_many_digits", "", 11]
@@ -1955,7 +2274,7 @@ function jslint_phase2_lex(state) {
1955
2274
  return char_after();
1956
2275
  }
1957
2276
  char_before();
1958
- if (read_digits("x", true) < 4) {
2277
+ if (read_digits("x", mode_digits_empty_string) < 4) {
1959
2278
 
1960
2279
  // test_cause:
1961
2280
  // ["\"\\u0\"", "char_after_escape", "expected_four_digits", "", 5]
@@ -1990,6 +2309,26 @@ function jslint_phase2_lex(state) {
1990
2309
  return char;
1991
2310
  }
1992
2311
 
2312
+ function check_numeric_separator(digits, column) {
2313
+
2314
+ // This function will check for illegal numeric-separator in <digits>.
2315
+
2316
+ digits.replace((
2317
+ jslint_rgx_numeric_separator_illegal
2318
+ ), function (ignore, ii) {
2319
+
2320
+ // test_cause:
2321
+ // ["0x0_0_;", "check_numeric_separator", "illegal_num_separator", "", 6]
2322
+ // ["0x0_0__0;", "check_numeric_separator", "illegal_num_separator", "", 6]
2323
+ // ["aa=1_2_;", "check_numeric_separator", "illegal_num_separator", "", 7]
2324
+ // ["aa=1_2__3;", "check_numeric_separator", "illegal_num_separator", "", 7]
2325
+ // ["aa=1_2_n;", "check_numeric_separator", "illegal_num_separator", "", 7]
2326
+
2327
+ warn_at("illegal_num_separator", line, column + ii + 1);
2328
+ return "";
2329
+ });
2330
+ }
2331
+
1993
2332
  function lex_comment() {
1994
2333
  let body;
1995
2334
  let ii = 0;
@@ -2028,12 +2367,12 @@ function jslint_phase2_lex(state) {
2028
2367
  // Lex/loop through each line until "*/".
2029
2368
 
2030
2369
  while (true) {
2031
- // rx_star_slash
2370
+ // jslint_rgx_star_slash
2032
2371
  ii = line_source.indexOf("*/");
2033
2372
  if (ii >= 0) {
2034
2373
  break;
2035
2374
  }
2036
- // rx_slash_star
2375
+ // jslint_rgx_slash_star
2037
2376
  ii = line_source.indexOf("/*");
2038
2377
  if (ii >= 0) {
2039
2378
 
@@ -2053,8 +2392,7 @@ function jslint_phase2_lex(state) {
2053
2392
  }
2054
2393
  }
2055
2394
  jj = line_source.slice(0, ii).search(
2056
- // rx_slash_star_or_slash
2057
- /\/\*|\/$/
2395
+ jslint_rgx_slash_star_or_slash
2058
2396
  );
2059
2397
  if (jj >= 0) {
2060
2398
 
@@ -2072,13 +2410,7 @@ function jslint_phase2_lex(state) {
2072
2410
 
2073
2411
  // Uncompleted work comment.
2074
2412
 
2075
- if (
2076
- !option_dict.devel
2077
- && (
2078
- // rx_todo
2079
- /\b(?:todo|TO\s?DO|HACK)\b/
2080
- ).test(snippet)
2081
- ) {
2413
+ if (!option_dict.devel && jslint_rgx_todo.test(snippet)) {
2082
2414
 
2083
2415
  // test_cause:
2084
2416
  // ["//todo", "lex_comment", "todo_comment", "(comment)", 1] //jslint-quiet
@@ -2090,10 +2422,7 @@ function jslint_phase2_lex(state) {
2090
2422
 
2091
2423
  [
2092
2424
  the_comment.directive, body
2093
- ] = Array.from(snippet.match(
2094
- // rx_directive
2095
- /^(jslint|property|global)\s+(.*)$/
2096
- ) || []).slice(1);
2425
+ ] = Array.from(snippet.match(jslint_rgx_directive) || []).slice(1);
2097
2426
  if (the_comment.directive === undefined) {
2098
2427
  return the_comment;
2099
2428
  }
@@ -2115,10 +2444,12 @@ function jslint_phase2_lex(state) {
2115
2444
  // Lex/loop through each directive in /*...*/
2116
2445
 
2117
2446
  ii = 0;
2118
- body.replace((
2119
- // rx_directive_part
2120
- /([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*|$/g
2121
- ), function (match0, key, val, jj) {
2447
+ body.replace(jslint_rgx_directive_part, function (
2448
+ match0,
2449
+ key,
2450
+ val,
2451
+ jj
2452
+ ) {
2122
2453
  if (ii !== jj) {
2123
2454
 
2124
2455
  // test_cause:
@@ -2192,13 +2523,7 @@ function jslint_phase2_lex(state) {
2192
2523
  // string.
2193
2524
 
2194
2525
  while (true) {
2195
- match = line_source.match(
2196
-
2197
- // Vim-hack - vim-editor has trouble parsing '`' in regexp
2198
-
2199
- // rx_mega
2200
- /[\u0060\\]|\$\{/
2201
- ) || {
2526
+ match = line_source.match(jslint_rgx_mega) || {
2202
2527
  "0": "",
2203
2528
  index: 0
2204
2529
  };
@@ -2275,12 +2600,16 @@ function jslint_phase2_lex(state) {
2275
2600
 
2276
2601
  function lex_number() {
2277
2602
  let prefix = snippet;
2603
+
2604
+ // PR-390 - Add numeric-separator check.
2605
+
2606
+ check_numeric_separator(prefix, column - prefix.length);
2278
2607
  char_after();
2279
2608
  switch (prefix === "0" && char) {
2280
2609
  case "b":
2281
2610
  case "o":
2282
2611
  case "x":
2283
- read_digits(char);
2612
+ read_digits(char, mode_digits_numeric_separator);
2284
2613
 
2285
2614
  // PR-351 - Ignore BigInt suffix 'n'.
2286
2615
 
@@ -2290,14 +2619,14 @@ function jslint_phase2_lex(state) {
2290
2619
  break;
2291
2620
  default:
2292
2621
  if (char === ".") {
2293
- read_digits("d");
2622
+ read_digits("d", mode_digits_numeric_separator);
2294
2623
  }
2295
2624
  if (char === "E" || char === "e") {
2296
2625
  char_after(char);
2297
2626
  if (char !== "+" && char !== "-") {
2298
2627
  char_before();
2299
2628
  }
2300
- read_digits("d");
2629
+ read_digits("d", mode_digits_numeric_separator);
2301
2630
  }
2302
2631
  }
2303
2632
 
@@ -2567,7 +2896,7 @@ function jslint_phase2_lex(state) {
2567
2896
  }
2568
2897
  break;
2569
2898
  case "{":
2570
- if (read_digits("d", true) === 0) {
2899
+ if (read_digits("d", mode_digits_empty_string) === 0) {
2571
2900
 
2572
2901
  // test_cause:
2573
2902
  // ["aa=/aa{/", "lex_regexp_group", "expected_a_before_b", ",", 8]
@@ -2580,7 +2909,7 @@ function jslint_phase2_lex(state) {
2580
2909
  // ["aa=/.{,/", "lex_regexp_group", "comma", "", 0]
2581
2910
 
2582
2911
  test_cause("comma");
2583
- read_digits("d", true);
2912
+ read_digits("d", mode_digits_empty_string);
2584
2913
  }
2585
2914
  if (char_after("}") === "?") {
2586
2915
 
@@ -2873,7 +3202,7 @@ function jslint_phase2_lex(state) {
2873
3202
  }
2874
3203
  }
2875
3204
  from = column;
2876
- match = line_source.match(rx_token);
3205
+ match = line_source.match(jslint_rgx_token);
2877
3206
 
2878
3207
  // match[1] token
2879
3208
  // match[2] whitespace
@@ -3193,7 +3522,7 @@ node --input-type=module --eval '
3193
3522
  // /\*jslint beta, node*\/
3194
3523
  import moduleHttps from "https";
3195
3524
  (async function () {
3196
- let dict = {};
3525
+ let dict = Object.create(null);
3197
3526
  let result = "";
3198
3527
  await new Promise(function (resolve) {
3199
3528
  moduleHttps.get((
@@ -3256,41 +3585,48 @@ import moduleHttps from "https";
3256
3585
  return true;
3257
3586
  }
3258
3587
 
3259
- function read_digits(base, quiet) {
3588
+ function read_digits(base, mode) {
3260
3589
  let digits = line_source.match(
3261
3590
  base === "b"
3262
- ? (
3263
- // rx_bits
3264
- /^[01]*/
3265
- )
3591
+ ? jslint_rgx_digits_bits
3266
3592
  : base === "o"
3267
- ? (
3268
- // rx_octals
3269
- /^[0-7]*/
3270
- )
3593
+ ? jslint_rgx_digits_octals
3271
3594
  : base === "x"
3272
- ? (
3273
- // rx_hexs
3274
- /^[0-9A-F]*/i
3275
- )
3276
- : (
3277
- // rx_digits
3278
- /^[0-9]*/
3279
- )
3595
+ ? jslint_rgx_digits_hexs
3596
+ : jslint_rgx_digits_decimals
3280
3597
  )[0];
3281
- let length = digits.length;
3282
- if (!quiet && length === 0) {
3598
+ if (
3599
+ (mode !== mode_digits_empty_string && digits.length === 0)
3600
+ || digits[0] === "_"
3601
+ ) {
3283
3602
 
3284
3603
  // test_cause:
3285
3604
  // ["0x", "read_digits", "expected_digits_after_a", "0x", 2]
3605
+ // ["0x_", "read_digits", "expected_digits_after_a", "0x", 2]
3286
3606
 
3287
3607
  warn_at("expected_digits_after_a", line, column, snippet);
3288
3608
  }
3289
- column += length;
3290
- line_source = line_source.slice(length);
3609
+
3610
+ // PR-390 - Add numeric-separator check.
3611
+
3612
+ if (mode === mode_digits_numeric_separator) {
3613
+ check_numeric_separator(digits, column);
3614
+ } else if (digits.indexOf("_") >= 0) {
3615
+
3616
+ // test_cause:
3617
+ // ["\"\\u{1_2}\"", "read_digits", "illegal_num_separator", "", 6]
3618
+
3619
+ warn_at(
3620
+ "illegal_num_separator",
3621
+ line,
3622
+ column + digits.indexOf("_") + 1
3623
+ );
3624
+ }
3625
+ column += digits.length;
3626
+ line_source = line_source.slice(digits.length);
3291
3627
  snippet += digits;
3292
3628
  char_after();
3293
- return length;
3629
+ return digits.length;
3294
3630
  }
3295
3631
 
3296
3632
  function read_line() {
@@ -3361,6 +3697,7 @@ import moduleHttps from "https";
3361
3697
  test_cause("line_disable");
3362
3698
  line_source = "";
3363
3699
  }
3700
+ // jslint_rgx_tab
3364
3701
  if (line_source.indexOf("\t") >= 0) {
3365
3702
  if (!option_dict.white) {
3366
3703
 
@@ -3369,10 +3706,7 @@ import moduleHttps from "https";
3369
3706
 
3370
3707
  warn_at("use_spaces", line, line_source.indexOf("\t") + 1);
3371
3708
  }
3372
- line_source = line_source.replace((
3373
- // rx_tab
3374
- /\t/g
3375
- ), " ");
3709
+ line_source = line_source.replace(jslint_rgx_tab, " ");
3376
3710
  }
3377
3711
  if (!option_dict.white && line_source.endsWith(" ")) {
3378
3712
 
@@ -3392,9 +3726,11 @@ import moduleHttps from "https";
3392
3726
  from,
3393
3727
  id,
3394
3728
  identifier: Boolean(identifier),
3729
+ is_fart: false,
3395
3730
  line,
3396
3731
  nr: token_list.length,
3397
- thru: column
3732
+ thru: column,
3733
+ value
3398
3734
  };
3399
3735
  token_list.push(the_token);
3400
3736
 
@@ -3404,12 +3740,6 @@ import moduleHttps from "https";
3404
3740
  mode_directive = false;
3405
3741
  }
3406
3742
 
3407
- // If the token is to have a value, give it one.
3408
-
3409
- if (value !== undefined) {
3410
- the_token.value = value;
3411
- }
3412
-
3413
3743
  // If this token is an identifier that touches a preceding number, or
3414
3744
  // a "/", comment, or regular expression literal that touches a preceding
3415
3745
  // comment or regular expression literal, then give a missing space warning.
@@ -3443,6 +3773,29 @@ import moduleHttps from "https";
3443
3773
  the_token.dot = true;
3444
3774
  }
3445
3775
 
3776
+ // PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart.
3777
+ // Farts are now detected by keeping a list of most recent "(" tokens at any
3778
+ // given depth. When a "=>" token is encountered, the most recent "(" token at
3779
+ // current depth is marked as a fart.
3780
+
3781
+ switch (id) {
3782
+ case "(":
3783
+ paren_backtrack_list[paren_depth] = the_token;
3784
+ paren_depth += 1;
3785
+ break;
3786
+ case ")":
3787
+ paren_depth -= 1;
3788
+ break;
3789
+ case "=>":
3790
+ if (
3791
+ token_prv_expr.id === ")"
3792
+ && paren_backtrack_list[paren_depth]
3793
+ ) {
3794
+ paren_backtrack_list[paren_depth].is_fart = true;
3795
+ }
3796
+ break;
3797
+ }
3798
+
3446
3799
  // The previous token is used to detect adjacency problems.
3447
3800
 
3448
3801
  token_prv = the_token;
@@ -3527,9 +3880,6 @@ function jslint_phase3_parse(state) {
3527
3880
  let catchage = catch_stack[0]; // The current catch-block.
3528
3881
  let functionage = token_global; // The current function.
3529
3882
  let mode_var; // "var" if using var; "let" if using let.
3530
- let rx_identifier = (
3531
- /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/
3532
- );
3533
3883
  let token_ii = 0; // The number of the next token.
3534
3884
  let token_now = token_global; // The current token being examined in
3535
3885
  // ... the parse.
@@ -3546,7 +3896,7 @@ function jslint_phase3_parse(state) {
3546
3896
  anon = token_now.id;
3547
3897
  } else if (
3548
3898
  token_now.id === "(string)"
3549
- && rx_identifier.test(token_now.value)
3899
+ && jslint_rgx_identifier.test(token_now.value)
3550
3900
  ) {
3551
3901
  anon = token_now.value;
3552
3902
  }
@@ -3558,7 +3908,7 @@ function jslint_phase3_parse(state) {
3558
3908
  match === undefined
3559
3909
 
3560
3910
  // test_cause:
3561
- // ["()", "advance", "expected_a_b", "(end)", 1]
3911
+ // ["{0:0}", "advance", "expected_a_b", "0", 2]
3562
3912
 
3563
3913
  ? stop("expected_a_b", token_nxt, id, artifact())
3564
3914
 
@@ -3607,7 +3957,7 @@ function jslint_phase3_parse(state) {
3607
3957
  // other assignment operators can modify, but they cannot initialize.
3608
3958
 
3609
3959
  const the_symbol = symbol(id, 20);
3610
- the_symbol.led = function (left) {
3960
+ the_symbol.led_infix = function (left) {
3611
3961
  const the_token = token_now;
3612
3962
  let right;
3613
3963
  the_token.arity = "assignment";
@@ -3919,7 +4269,7 @@ function jslint_phase3_parse(state) {
3919
4269
 
3920
4270
  const the_symbol = symbol(id);
3921
4271
  the_symbol.constant = true;
3922
- the_symbol.nud = (
4272
+ the_symbol.nud_prefix = (
3923
4273
  typeof value === "function"
3924
4274
  ? value
3925
4275
  : function () {
@@ -4113,7 +4463,7 @@ function jslint_phase3_parse(state) {
4113
4463
  // Create an infix operator.
4114
4464
 
4115
4465
  const the_symbol = symbol(id, bp);
4116
- the_symbol.led = function (left) {
4466
+ the_symbol.led_infix = function (left) {
4117
4467
  const the_token = token_now;
4118
4468
  the_token.arity = "binary";
4119
4469
  if (f !== undefined) {
@@ -4171,12 +4521,12 @@ function jslint_phase3_parse(state) {
4171
4521
  return the_token;
4172
4522
  }
4173
4523
 
4174
- function infix_fart_unwrapped(left) {
4524
+ function infix_fart_unwrapped() {
4175
4525
 
4176
4526
  // test_cause:
4177
- // ["aa=>0", "infix_fart_unwrapped", "wrap_parameter", "aa", 1]
4527
+ // ["aa=>0", "infix_fart_unwrapped", "wrap_fart_parameter", "=>", 3]
4178
4528
 
4179
- return stop("wrap_parameter", left);
4529
+ return stop("wrap_fart_parameter", token_now);
4180
4530
  }
4181
4531
 
4182
4532
  function infix_grave(left) {
@@ -4196,7 +4546,7 @@ function jslint_phase3_parse(state) {
4196
4546
  let the_subscript = parse_expression(0);
4197
4547
  if (the_subscript.id === "(string)" || the_subscript.id === "`") {
4198
4548
  name = survey(the_subscript);
4199
- if (rx_identifier.test(name)) {
4549
+ if (jslint_rgx_identifier.test(name)) {
4200
4550
 
4201
4551
  // test_cause:
4202
4552
  // ["aa[`aa`]", "infix_lbracket", "subscript_a", "aa", 4]
@@ -4339,13 +4689,13 @@ function jslint_phase3_parse(state) {
4339
4689
  // Create a right associative infix operator.
4340
4690
 
4341
4691
  const the_symbol = symbol(id, bp);
4342
- the_symbol.led = function parse_infixr_led(left) {
4692
+ the_symbol.led_infix = function parse_infixr_led(left) {
4343
4693
  const the_token = token_now;
4344
4694
 
4345
4695
  // test_cause:
4346
- // ["0**0", "parse_infixr_led", "led", "", 0]
4696
+ // ["0**0", "parse_infixr_led", "led_infix", "", 0]
4347
4697
 
4348
- test_cause("led");
4698
+ test_cause("led_infix");
4349
4699
  the_token.arity = "binary";
4350
4700
  the_token.expression = [left, parse_expression(bp - 1)];
4351
4701
  return the_token;
@@ -4353,39 +4703,70 @@ function jslint_phase3_parse(state) {
4353
4703
  return the_symbol;
4354
4704
  }
4355
4705
 
4356
- function lookahead() {
4357
-
4358
- // Look ahead one token without advancing, skipping comments.
4359
-
4360
- let cadet;
4361
- let ii = token_ii;
4362
- while (true) {
4363
- cadet = token_list[ii];
4364
- if (cadet.id !== "(comment)") {
4365
- return cadet;
4366
- }
4367
- ii += 1;
4368
- }
4369
- }
4370
-
4371
4706
  function parse_expression(rbp, initial) {
4372
4707
 
4373
4708
  // This is the heart of JSLINT, the Pratt parser. In addition to parsing, it
4374
- // is looking for ad hoc lint patterns. We add .fud to Pratt's model, which is
4375
- // like .nud except that it is only used on the first token of a statement.
4376
- // Having .fud makes it much easier to define statement-oriented languages like
4377
- // JavaScript. I retained Pratt's nomenclature.
4709
+ // is looking for ad hoc lint patterns. We add .fud_stmt to Pratt's model, which
4710
+ // is like .nud_prefix except that it is only used on the first token of a
4711
+ // statement. Having .fud_stmt makes it much easier to define statement-oriented
4712
+ // languages like JavaScript. I retained Pratt's nomenclature.
4378
4713
  // They are elements of the parsing method called Top Down Operator Precedence.
4379
4714
 
4380
- // .nud Null denotation
4381
- // .fud First null denotation
4382
- // .led Left denotation
4383
- // lbp Left binding power
4384
- // rbp Right binding power
4385
-
4386
- // It processes a nud (variable, constant, prefix operator). It will then
4387
- // process leds (infix operators) until the bind powers cause it to stop. It
4388
- // returns the expression's parse tree.
4715
+ // .nud_prefix Null denotation. The prefix handler.
4716
+ // .fud_stmt First null denotation. The statement handler.
4717
+ // .led_infix Left denotation. The infix/postfix handler.
4718
+ // lbp Left binding power of infix operator. It tells us how strongly
4719
+ // the operator binds to the argument at its left.
4720
+ // rbp Right binding power.
4721
+
4722
+ // It processes a nud_prefix (variable, constant, prefix operator). It will then
4723
+ // process leds (infix operators) until the bind powers cause it to stop (it
4724
+ // consumes tokens until it meets a token whose lbp <= rbp). Specifically, it
4725
+ // means that it collects all tokens that bind together before returning to the
4726
+ // operator that called it. It returns the expression's parse tree.
4727
+
4728
+ // For example, "3 + 1 * 2 * 4 + 5"
4729
+ // parses into
4730
+ // {
4731
+ // "id": "+",
4732
+ // "expression": [
4733
+ // {
4734
+ // "id": "+",
4735
+ // "expression": [
4736
+ // {
4737
+ // "id": "(number)",
4738
+ // "value": "3"
4739
+ // },
4740
+ // {
4741
+ // "id": "*",
4742
+ // "expression": [
4743
+ // {
4744
+ // "id": "*",
4745
+ // "expression": [
4746
+ // {
4747
+ // "id": "(number)",
4748
+ // "value": "1"
4749
+ // },
4750
+ // {
4751
+ // "id": "(number)",
4752
+ // "value": "2"
4753
+ // }
4754
+ // ]
4755
+ // },
4756
+ // {
4757
+ // "id": "(number)",
4758
+ // "value": "4"
4759
+ // }
4760
+ // ]
4761
+ // }
4762
+ // ]
4763
+ // },
4764
+ // {
4765
+ // "id": "(number)",
4766
+ // "value": "5"
4767
+ // }
4768
+ // ]
4769
+ // }
4389
4770
 
4390
4771
  let left;
4391
4772
  let the_symbol;
@@ -4397,13 +4778,13 @@ function jslint_phase3_parse(state) {
4397
4778
  advance();
4398
4779
  }
4399
4780
  the_symbol = syntax_dict[token_now.id];
4400
- if (the_symbol !== undefined && the_symbol.nud !== undefined) {
4781
+ if (the_symbol !== undefined && the_symbol.nud_prefix !== undefined) {
4401
4782
 
4402
4783
  // test_cause:
4403
4784
  // ["0", "parse_expression", "symbol", "", 0]
4404
4785
 
4405
4786
  test_cause("symbol");
4406
- left = the_symbol.nud();
4787
+ left = the_symbol.nud_prefix();
4407
4788
  } else if (token_now.identifier) {
4408
4789
 
4409
4790
  // test_cause:
@@ -4428,32 +4809,38 @@ function jslint_phase3_parse(state) {
4428
4809
  the_symbol = syntax_dict[token_nxt.id];
4429
4810
  if (
4430
4811
  the_symbol === undefined
4431
- || the_symbol.led === undefined
4812
+ || the_symbol.led_infix === undefined
4432
4813
  || the_symbol.lbp <= rbp
4433
4814
  ) {
4434
4815
  break;
4435
4816
  }
4436
4817
  advance();
4437
- left = the_symbol.led(left);
4818
+ left = the_symbol.led_infix(left);
4438
4819
  }
4439
4820
  return left;
4440
4821
  }
4441
4822
 
4442
- function parse_fart(pl) {
4823
+ function parse_fart() {
4824
+ let parameters;
4825
+ let signature;
4443
4826
  let the_fart;
4827
+ [parameters, signature] = prefix_function_arg();
4444
4828
  advance("=>");
4445
4829
  the_fart = token_now;
4446
4830
  the_fart.arity = "binary";
4447
4831
  the_fart.name = "=>";
4448
4832
  the_fart.level = functionage.level + 1;
4449
4833
  function_list.push(the_fart);
4450
- if (functionage.loop > 0) {
4451
4834
 
4452
- // test_cause:
4453
- // ["while(0){aa.map(()=>0);}", "parse_fart", "function_in_loop", "=>", 19]
4835
+ // PR-384 - Relax warning "function_in_loop".
4836
+ //
4837
+ // if (functionage.loop > 0) {
4454
4838
 
4455
- warn("function_in_loop", the_fart);
4456
- }
4839
+ // // test_cause:
4840
+ // // ["while(0){aa.map(()=>0);}", "parse_fart", "function_in_loop", "=>", 19]
4841
+ //
4842
+ // warn("function_in_loop", the_fart);
4843
+ // }
4457
4844
 
4458
4845
  // Give the function properties storing its names and for observing the depth
4459
4846
  // of loops and switches.
@@ -4461,6 +4848,8 @@ function jslint_phase3_parse(state) {
4461
4848
  the_fart.context = empty();
4462
4849
  the_fart.finally = 0;
4463
4850
  the_fart.loop = 0;
4851
+ the_fart.parameters = parameters;
4852
+ the_fart.signature = signature;
4464
4853
  the_fart.switch = 0;
4465
4854
  the_fart.try = 0;
4466
4855
 
@@ -4468,23 +4857,42 @@ function jslint_phase3_parse(state) {
4468
4857
 
4469
4858
  function_stack.push(functionage);
4470
4859
  functionage = the_fart;
4471
- the_fart.parameters = pl[0];
4472
- the_fart.signature = pl[1];
4473
- the_fart.parameters.forEach(function (name) {
4860
+ the_fart.parameters.forEach(function enroll_parameter(name) {
4861
+ if (name.identifier) {
4862
+ enroll(name, "parameter", true);
4863
+ } else {
4864
+
4865
+ // PR-385 - Bugfix - Fixes issue #382 - fix warnings against destructured fart.
4474
4866
 
4475
4867
  // test_cause:
4476
- // ["(aa)=>{}", "parse_fart", "parameter", "", 0]
4868
+ // ["([aa])=>0", "enroll_parameter", "use_function_not_fart", "=>", 7]
4869
+ // ["({aa})=>0", "enroll_parameter", "use_function_not_fart", "=>", 7]
4477
4870
 
4478
- test_cause("parameter");
4479
- enroll(name, "parameter", true);
4871
+ warn("use_function_not_fart", the_fart);
4872
+
4873
+ // Recurse enroll_parameter().
4874
+
4875
+ name.names.forEach(enroll_parameter);
4876
+ }
4480
4877
  });
4481
4878
  if (token_nxt.id === "{") {
4482
4879
 
4483
4880
  // test_cause:
4484
- // ["()=>{}", "parse_fart", "expected_a_b", "=>", 3]
4881
+ // ["()=>{}", "parse_fart", "use_function_not_fart", "=>", 3]
4485
4882
 
4486
- warn("expected_a_b", the_fart, "function", "=>");
4883
+ warn("use_function_not_fart", the_fart);
4487
4884
  the_fart.block = block("body");
4885
+ } else if (
4886
+ syntax_dict[token_nxt.id] !== undefined
4887
+ && syntax_dict[token_nxt.id].fud_stmt !== undefined
4888
+ ) {
4889
+
4890
+ // PR-384 - Bugfix - Fixes issue #379 - warn against naked-statement in fart.
4891
+
4892
+ // test_cause:
4893
+ // ["()=>delete aa", "parse_fart", "unexpected_a_after_b", "=>", 5]
4894
+
4895
+ stop("unexpected_a_after_b", token_nxt, token_nxt.id, "=>");
4488
4896
  } else {
4489
4897
  the_fart.expression = parse_expression(0);
4490
4898
  }
@@ -4499,13 +4907,7 @@ function jslint_phase3_parse(state) {
4499
4907
  let negative;
4500
4908
  switch (token_nxt.id) {
4501
4909
  case "(number)":
4502
- if (!(
4503
-
4504
- // https://datatracker.ietf.org/doc/html/rfc7159#section-6
4505
- // number = [ minus ] int [ frac ] [ exp ]
4506
-
4507
- /^-?(?:0|[1-9]\d*?)(?:\.\d*?)?(?:[eE][+\-]?\d+?)?$/
4508
- ).test(token_nxt.value)) {
4910
+ if (!jslint_rgx_json_number.test(token_nxt.value)) {
4509
4911
 
4510
4912
  // test_cause:
4511
4913
  // ["[-.0]", "parse_json", "unexpected_a", ".", 3]
@@ -4707,7 +5109,7 @@ function jslint_phase3_parse(state) {
4707
5109
  the_symbol = syntax_dict[first.id];
4708
5110
  if (
4709
5111
  the_symbol !== undefined
4710
- && the_symbol.fud !== undefined
5112
+ && the_symbol.fud_stmt !== undefined
4711
5113
 
4712
5114
  // PR-318 - Bugfix - Fixes issues #316, #317 - dynamic-import().
4713
5115
 
@@ -4716,7 +5118,7 @@ function jslint_phase3_parse(state) {
4716
5118
  the_symbol.disrupt = false;
4717
5119
  the_symbol.statement = true;
4718
5120
  token_now.arity = "statement";
4719
- the_statement = the_symbol.fud();
5121
+ the_statement = the_symbol.fud_stmt();
4720
5122
  functionage.statement_prv = the_statement;
4721
5123
  } else {
4722
5124
 
@@ -4786,7 +5188,7 @@ function jslint_phase3_parse(state) {
4786
5188
  // Create one of the postassign operators.
4787
5189
 
4788
5190
  const the_symbol = symbol(id, 150);
4789
- the_symbol.led = function (left) {
5191
+ the_symbol.led_infix = function (left) {
4790
5192
  token_now.expression = left;
4791
5193
  token_now.arity = "postassign";
4792
5194
  check_mutation(token_now.expression);
@@ -4800,7 +5202,7 @@ function jslint_phase3_parse(state) {
4800
5202
  // Create one of the preassign operators.
4801
5203
 
4802
5204
  const the_symbol = symbol(id);
4803
- the_symbol.nud = function () {
5205
+ the_symbol.nud_prefix = function () {
4804
5206
  const the_token = token_now;
4805
5207
  the_token.arity = "preassign";
4806
5208
  the_token.expression = parse_expression(150);
@@ -4815,7 +5217,7 @@ function jslint_phase3_parse(state) {
4815
5217
  // Create a prefix operator.
4816
5218
 
4817
5219
  const the_symbol = symbol(id);
4818
- the_symbol.nud = function () {
5220
+ the_symbol.nud_prefix = function () {
4819
5221
  const the_token = token_now;
4820
5222
  the_token.arity = "unary";
4821
5223
  if (typeof f === "function") {
@@ -4997,6 +5399,9 @@ function jslint_phase3_parse(state) {
4997
5399
  if (name.identifier) {
4998
5400
  enroll(name, "parameter", false);
4999
5401
  } else {
5402
+
5403
+ // Recurse enroll_parameter().
5404
+
5000
5405
  name.names.forEach(enroll_parameter);
5001
5406
  }
5002
5407
  });
@@ -5457,25 +5862,14 @@ function jslint_phase3_parse(state) {
5457
5862
  }
5458
5863
 
5459
5864
  function prefix_lparen() {
5460
- const cadet = lookahead().id;
5461
- const the_paren = token_now;
5865
+ let the_paren = token_now;
5462
5866
  let the_value;
5463
5867
 
5464
- // We can distinguish between a parameter list for => and a wrapped expression
5465
- // with one token of lookahead.
5466
-
5467
- if (
5468
- token_nxt.id === ")"
5469
- || token_nxt.id === "..."
5470
- || (token_nxt.identifier && (cadet === "," || cadet === "="))
5471
- ) {
5472
-
5473
- // test_cause:
5474
- // ["()=>0", "prefix_lparen", "fart", "", 0]
5868
+ // PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart.
5475
5869
 
5476
- test_cause("fart");
5870
+ if (token_now.is_fart) {
5477
5871
  the_paren.free = false;
5478
- return parse_fart(prefix_function_arg());
5872
+ return parse_fart();
5479
5873
  }
5480
5874
 
5481
5875
  // test_cause:
@@ -5493,31 +5887,6 @@ function jslint_phase3_parse(state) {
5493
5887
  }
5494
5888
  the_value.wrapped = true;
5495
5889
  advance(")", the_paren);
5496
- if (token_nxt.id === "=>") {
5497
- if (the_value.arity !== "variable") {
5498
- if (the_value.id === "{" || the_value.id === "[") {
5499
-
5500
- // test_cause:
5501
- // ["([])=>0", "prefix_lparen", "expected_a_before_b", "(", 1]
5502
- // ["({})=>0", "prefix_lparen", "expected_a_before_b", "(", 1]
5503
-
5504
- warn("expected_a_before_b", the_paren, "function", "(");
5505
-
5506
- // test_cause:
5507
- // ["([])=>0", "prefix_lparen", "expected_a_b", "=>", 5]
5508
- // ["({})=>0", "prefix_lparen", "expected_a_b", "=>", 5]
5509
-
5510
- return stop("expected_a_b", token_nxt, "{", "=>");
5511
- }
5512
-
5513
- // test_cause:
5514
- // ["(0)=>0", "prefix_lparen", "expected_identifier_a", "0", 2]
5515
-
5516
- return stop("expected_identifier_a", the_value);
5517
- }
5518
- the_paren.expression = [the_value];
5519
- return parse_fart([the_paren.expression, "(" + the_value.id + ")"]);
5520
- }
5521
5890
  return the_value;
5522
5891
  }
5523
5892
 
@@ -5598,12 +5967,12 @@ function jslint_phase3_parse(state) {
5598
5967
  anon = "anonymous";
5599
5968
  }
5600
5969
 
5601
- function stmt(id, fud) {
5970
+ function stmt(id, fud_stmt) {
5602
5971
 
5603
5972
  // Create a statement.
5604
5973
 
5605
5974
  const the_symbol = symbol(id);
5606
- the_symbol.fud = fud;
5975
+ the_symbol.fud_stmt = fud_stmt;
5607
5976
  return the_symbol;
5608
5977
  }
5609
5978
 
@@ -6038,10 +6407,7 @@ function jslint_phase3_parse(state) {
6038
6407
  advance("from");
6039
6408
  advance("(string)");
6040
6409
  the_import.import = token_now;
6041
- if (!(
6042
- // rx_module
6043
- /^[a-zA-Z0-9_$:.@\-\/]+$/
6044
- ).test(token_now.value)) {
6410
+ if (!jslint_rgx_module.test(token_now.value)) {
6045
6411
 
6046
6412
  // test_cause:
6047
6413
  // ["import aa from \"!aa\"", "stmt_import", "bad_module_name_a", "!aa", 16]
@@ -6652,13 +7018,13 @@ function jslint_phase3_parse(state) {
6652
7018
 
6653
7019
  if (id === "(string)") {
6654
7020
  id = name.value;
6655
- if (!rx_identifier.test(id)) {
7021
+ if (!jslint_rgx_identifier.test(id)) {
6656
7022
  return id;
6657
7023
  }
6658
7024
  } else if (id === "`") {
6659
7025
  if (name.value.length === 1) {
6660
7026
  id = name.value[0].value;
6661
- if (!rx_identifier.test(id)) {
7027
+ if (!jslint_rgx_identifier.test(id)) {
6662
7028
  return id;
6663
7029
  }
6664
7030
  }
@@ -6691,10 +7057,7 @@ function jslint_phase3_parse(state) {
6691
7057
  } else if (
6692
7058
  !option_dict.name
6693
7059
  && name.identifier
6694
- && (
6695
- // rx_weird_property
6696
- /^_|\$|Sync$|_$/m
6697
- ).test(id)
7060
+ && jslint_rgx_weird_property.test(id)
6698
7061
  ) {
6699
7062
 
6700
7063
  // test_cause:
@@ -6732,7 +7095,7 @@ function jslint_phase3_parse(state) {
6732
7095
  // Create a ternary operator.
6733
7096
 
6734
7097
  const the_symbol = symbol(id1, 30);
6735
- the_symbol.led = function parse_ternary_led(left) {
7098
+ the_symbol.led_infix = function parse_ternary_led(left) {
6736
7099
  const the_token = token_now;
6737
7100
  let second;
6738
7101
  second = parse_expression(20);
@@ -7448,10 +7811,7 @@ function jslint_phase4_walk(state) {
7448
7811
  test_cause("cack");
7449
7812
  cack = !cack;
7450
7813
  }
7451
- if ((
7452
- // rx_cap
7453
- /^[A-Z]/
7454
- ).test(left.name.id) !== cack) {
7814
+ if (jslint_rgx_cap.test(left.name.id) !== cack) {
7455
7815
  if (the_new !== undefined) {
7456
7816
 
7457
7817
  // test_cause:
@@ -7628,6 +7988,11 @@ function jslint_phase4_walk(state) {
7628
7988
  || thing.expression[0].constant === true
7629
7989
  || is_equal(thing.expression[1], thing.expression[2])
7630
7990
  ) {
7991
+
7992
+ // test_cause:
7993
+ // ["let aa=(aa?`${0}`:`${0}`);", "post_t", "unexpected_a", "?", 11]
7994
+ // ["let aa=(aa?`0`:`0`);", "post_t", "unexpected_a", "?", 11]
7995
+
7631
7996
  warn("unexpected_a", thing);
7632
7997
  } else if (is_equal(thing.expression[0], thing.expression[1])) {
7633
7998
 
@@ -8847,6 +9212,7 @@ function jslint_report({
8847
9212
 
8848
9213
  // This function will create human-readable, html-report
8849
9214
  // for warnings, properties, and functions from jslint-result-object.
9215
+ //
8850
9216
  // Example usage:
8851
9217
  // let result = jslint("console.log('hello world')");
8852
9218
  // let html = jslint_report(result);
@@ -8879,7 +9245,6 @@ function jslint_report({
8879
9245
  );
8880
9246
  }
8881
9247
 
8882
- html += "<div class=\"JSLINT_\" id=\"JSLINT_REPORT_HTML\">\n";
8883
9248
  html += String(`
8884
9249
  <style class="JSLINT_REPORT_STYLE">
8885
9250
  /* jslint utility2:true */
@@ -9028,19 +9393,38 @@ pyNj+JctcQLXenBOCms46aMkenIx45WpXqxxVJQLz/vgpmAVa0fmDv6Pue9xVTBPfVxCUGfj\
9028
9393
  /7xoEqvL+2E8VOyCTuT/7j269Zy4jUtN+g4="
9029
9394
  ) format("woff2");
9030
9395
  }
9031
- *,
9032
- *:after,
9033
- *:before {
9396
+ .JSLINT_,
9397
+ .JSLINT_ address,
9398
+ .JSLINT_ button,
9399
+ .JSLINT_ cite,
9400
+ .JSLINT_ dd,
9401
+ .JSLINT_ dfn,
9402
+ .JSLINT_ dl,
9403
+ .JSLINT_ dt,
9404
+ .JSLINT_ fieldset,
9405
+ .JSLINT_ fieldset > div,
9406
+ .JSLINT_ input,
9407
+ .JSLINT_ label,
9408
+ .JSLINT_ legend,
9409
+ .JSLINT_ ol,
9410
+ .JSLINT_ samp,
9411
+ .JSLINT_ style,
9412
+ .JSLINT_ textarea,
9413
+ .JSLINT_ ul {
9034
9414
  border: 0;
9035
9415
  box-sizing: border-box;
9036
9416
  margin: 0;
9037
9417
  padding: 0;
9038
9418
  }
9419
+ /* disable text inflation algorithm used on some smartphones and tablets */
9039
9420
  .JSLINT_ {
9040
9421
  -ms-text-size-adjust: none;
9041
9422
  -webkit-text-size-adjust: none;
9042
9423
  text-size-adjust: none;
9043
9424
  }
9425
+ .JSLINT_REPORT_ div {
9426
+ box-sizing: border-box;
9427
+ }
9044
9428
  /*csslint ignore:end*/
9045
9429
 
9046
9430
  /* css - jslint_report - font */
@@ -9074,7 +9458,7 @@ pyNj+JctcQLXenBOCms46aMkenIx45WpXqxxVJQLz/vgpmAVa0fmDv6Pue9xVTBPfVxCUGfj\
9074
9458
  }
9075
9459
 
9076
9460
  /* css - jslint_report - general */
9077
- body {
9461
+ .JSLINT_ {
9078
9462
  background: antiquewhite;
9079
9463
  }
9080
9464
  .JSLINT_ fieldset {
@@ -9320,6 +9704,9 @@ body {
9320
9704
  : "global"
9321
9705
  );
9322
9706
  if (global.length + froms.length + exports.length > 0) {
9707
+ if (functions.length === 0) {
9708
+ html += "<br>\n";
9709
+ }
9323
9710
  html += "<div class=\"level level0\">\n";
9324
9711
  html += detail(module, global);
9325
9712
  html += detail("import from", froms);
@@ -9408,7 +9795,6 @@ body {
9408
9795
  });
9409
9796
  html += "</div>\n";
9410
9797
  html += "</fieldset>\n";
9411
- html += "</div>\n";
9412
9798
  return html;
9413
9799
  }
9414
9800
 
@@ -9484,14 +9870,14 @@ function jstestIt(description, testFunction, mode) {
9484
9870
  }));
9485
9871
  }
9486
9872
 
9487
- function jstestOnExit(exitCode, processExit, countFailed) {
9873
+ function jstestOnExit(exitCode, mode) {
9488
9874
 
9489
9875
  // This function will on process-exit, print test-report
9490
9876
  // and exit with non-zero exit-code if any test failed.
9491
9877
 
9492
9878
  let message = (
9493
9879
  (
9494
- (jstestCountFailed || countFailed)
9880
+ (jstestCountFailed || mode === "testsFailed")
9495
9881
  ? "\n\u001b[31m"
9496
9882
  : "\n\u001b[32m"
9497
9883
  )
@@ -9499,11 +9885,10 @@ function jstestOnExit(exitCode, processExit, countFailed) {
9499
9885
  + " tests failed - " + jstestCountFailed + "\n"
9500
9886
  + "\u001b[39m"
9501
9887
  );
9502
- if (!processExit) {
9888
+ if (mode !== "testsFailed") {
9503
9889
  console.error(message);
9504
- processExit = process.exit;
9505
9890
  }
9506
- processExit(exitCode || jstestCountFailed);
9891
+ process.exitCode = exitCode || jstestCountFailed;
9507
9892
  return message;
9508
9893
  }
9509
9894
 
@@ -9569,7 +9954,7 @@ function objectDeepCopyWithKeysSorted(obj) {
9569
9954
 
9570
9955
  // Recursively deep-copy obj with keys sorted.
9571
9956
 
9572
- sorted = {};
9957
+ sorted = Object.create(null);
9573
9958
  Object.keys(obj).sort().forEach(function (key) {
9574
9959
  sorted[key] = objectDeepCopyWithKeysSorted(obj[key]);
9575
9960
  });
@@ -10225,11 +10610,11 @@ async function v8CoverageReportCreate({
10225
10610
  // 3. Create html-coverage-reports in <coverageDir>.
10226
10611
 
10227
10612
  let cwd;
10613
+ let excludeList = [];
10228
10614
  let exitCode = 0;
10229
10615
  let fileDict;
10230
- let fileExcludeList = [];
10231
- let fileIncludeList = [];
10232
- let fileIncludeNodeModules;
10616
+ let includeList = [];
10617
+ let modeIncludeNodeModules;
10233
10618
  let processArgElem;
10234
10619
  let promiseList = [];
10235
10620
  let v8CoverageObj;
@@ -10254,9 +10639,19 @@ async function v8CoverageReportCreate({
10254
10639
  <style>
10255
10640
  /* jslint utility2:true */
10256
10641
  /*csslint ignore:start*/
10257
- * {
10258
- box-sizing: border-box;
10259
- font-family: consolas, menlo, monospace;
10642
+ .coverage,
10643
+ .coverage a,
10644
+ .coverage div,
10645
+ .coverage pre,
10646
+ .coverage span,
10647
+ .coverage table,
10648
+ .coverage tbody,
10649
+ .coverage td,
10650
+ .coverage th,
10651
+ .coverage thead,
10652
+ .coverage tr {
10653
+ box-sizing: border-box;
10654
+ font-family: monospace;
10260
10655
  }
10261
10656
  /*csslint ignore:end*/
10262
10657
 
@@ -10402,6 +10797,7 @@ body {
10402
10797
  }
10403
10798
  txtBorder = (
10404
10799
  "+" + "-".repeat(padPathname + 2) + "+"
10800
+ + "-".repeat(padLines + 2) + "+"
10405
10801
  + "-".repeat(padLines + 2) + "+\n"
10406
10802
  );
10407
10803
  txt = "";
@@ -10409,7 +10805,8 @@ body {
10409
10805
  txt += txtBorder;
10410
10806
  txt += (
10411
10807
  "| " + String("Files covered").padEnd(padPathname, " ") + " | "
10412
- + String("Lines").padStart(padLines, " ") + " |\n"
10808
+ + String("Lines").padStart(padLines, " ") + " | "
10809
+ + String("Remaining").padStart(padLines, " ") + " |\n"
10413
10810
  );
10414
10811
  txt += txtBorder;
10415
10812
  fileList.forEach(function ({
@@ -10489,7 +10886,8 @@ body {
10489
10886
  + String("./" + pathname).padEnd(padPathname, " ") + " | "
10490
10887
  + String(
10491
10888
  modeCoverageIgnoreFile + " " + coveragePct + " %"
10492
- ).padStart(padLines, " ") + " |\n"
10889
+ ).padStart(padLines, " ") + " | "
10890
+ + " ".repeat(padLines) + " |\n"
10493
10891
  );
10494
10892
  txt += (
10495
10893
  "| " + "*".repeat(
@@ -10497,6 +10895,9 @@ body {
10497
10895
  ).padEnd(padPathname, "_") + " | "
10498
10896
  + String(
10499
10897
  linesCovered + " / " + linesTotal
10898
+ ).padStart(padLines, " ") + " | "
10899
+ + String(
10900
+ (linesTotal - linesCovered) + " / " + linesTotal
10500
10901
  ).padStart(padLines, " ") + " |\n"
10501
10902
  );
10502
10903
  txt += txtBorder;
@@ -10660,21 +11061,6 @@ ${String(count || "-0").padStart(7, " ")}
10660
11061
  ), txt));
10661
11062
  }
10662
11063
 
10663
- function pathnameRelativeCwd(pathname) {
10664
-
10665
- // This function will if <pathname> is inside <cwd>,
10666
- // return it relative to <cwd>, else empty-string.
10667
-
10668
- pathname = modulePath.resolve(pathname).replace((
10669
- /\\/g
10670
- ), "/");
10671
- if (!pathname.startsWith(cwd)) {
10672
- return;
10673
- }
10674
- pathname = pathname.slice(cwd.length);
10675
- return pathname;
10676
- }
10677
-
10678
11064
  /*
10679
11065
  function sentinel() {}
10680
11066
  */
@@ -10706,28 +11092,28 @@ function sentinel() {}
10706
11092
  processArgElem[1] = processArgElem.slice(1).join("=");
10707
11093
  switch (processArgElem[0]) {
10708
11094
 
10709
- // PR-371 - add cli-option `--exclude=aa,bb`
11095
+ // PR-371
11096
+ // Add cli-option `--exclude=...`.
10710
11097
 
10711
11098
  case "--exclude":
10712
- fileExcludeList = fileExcludeList.concat(
10713
- processArgElem[1].split(",")
10714
- );
11099
+ excludeList.push(processArgElem[1]);
10715
11100
  break;
10716
11101
 
10717
- // PR-371 - add cli-option `--exclude-node-modules=false`
11102
+ // PR-371
11103
+ // Add cli-option `--include=...`
10718
11104
 
10719
- case "--exclude-node-modules":
10720
- fileIncludeNodeModules = (
10721
- /0|false|null|undefined/
10722
- ).test(processArgElem[1]);
11105
+ case "--include":
11106
+ includeList.push(processArgElem[1]);
10723
11107
  break;
10724
11108
 
10725
- // PR-371 - add cli-option `--include=aa,bb`
11109
+ // PR-400
11110
+ // Disable default-coverage of directory `node_modules`,
11111
+ // but allow override with cli-option `--include-node-modules=1`.
10726
11112
 
10727
- case "--include":
10728
- fileIncludeList = fileIncludeList.concat(
10729
- processArgElem[1].split(",")
10730
- );
11113
+ case "--include-node-modules":
11114
+ modeIncludeNodeModules = !(
11115
+ /0|false|null|undefined/
11116
+ ).test(processArgElem[1]);
10731
11117
  break;
10732
11118
  }
10733
11119
  }
@@ -10781,9 +11167,11 @@ function sentinel() {}
10781
11167
  ).test(file);
10782
11168
  });
10783
11169
  v8CoverageObj = await Promise.all(v8CoverageObj.map(async function (file) {
10784
- let data = await moduleFs.promises.readFile(coverageDir + file, "utf8");
11170
+ let data;
11171
+ let pathnameDict = Object.create(null);
11172
+ data = await moduleFs.promises.readFile(coverageDir + file, "utf8");
10785
11173
  data = JSON.parse(data);
10786
- data.result = data.result.filter(function (scriptCov) {
11174
+ data.result.forEach(function (scriptCov) {
10787
11175
  let pathname = scriptCov.url;
10788
11176
 
10789
11177
  // Filter out internal coverages.
@@ -10794,38 +11182,40 @@ function sentinel() {}
10794
11182
 
10795
11183
  // Normalize pathname.
10796
11184
 
10797
- pathname = pathnameRelativeCwd(moduleUrl.fileURLToPath(pathname));
10798
- if (
11185
+ pathname = moduleUrl.fileURLToPath(pathname);
11186
+ pathname = modulePath.resolve(pathname).replace((
11187
+ /\\/g
11188
+ ), "/");
10799
11189
 
10800
11190
  // Filter files outside of cwd.
10801
11191
 
10802
- !pathname
10803
- || pathname.startsWith("[")
11192
+ if (pathname.indexOf("[") >= 0 || !pathname.startsWith(cwd)) {
11193
+ return;
11194
+ }
10804
11195
 
10805
- // PR-371 - Filter directory node_modules.
11196
+ // Normalize pathname relative to cwd.
10806
11197
 
10807
- || (
10808
- !fileIncludeNodeModules
10809
- && (
10810
- /(?:^|\/)node_modules\//m
10811
- ).test(pathname)
10812
- )
11198
+ pathname = pathname.slice(cwd.length);
11199
+ scriptCov.url = pathname;
11200
+ pathnameDict[pathname] = scriptCov;
11201
+ });
10813
11202
 
10814
- // PR-371 - Filter fileExcludeList.
11203
+ // PR-400
11204
+ // Filter directory `node_modules`.
10815
11205
 
10816
- || fileExcludeList.indexOf(pathname) >= 0
11206
+ if (!modeIncludeNodeModules) {
11207
+ excludeList.push("node_modules/");
11208
+ }
10817
11209
 
10818
- // PR-371 - Filter fileIncludeList.
11210
+ // PR-400
11211
+ // Filter files by glob-patterns in excludeList, includeList.
10819
11212
 
10820
- || (
10821
- fileIncludeList.length > 0
10822
- && fileIncludeList.indexOf(pathname) === -1
10823
- )
10824
- ) {
10825
- return;
10826
- }
10827
- scriptCov.url = pathname;
10828
- return true;
11213
+ data.result = globExclude({
11214
+ excludeList,
11215
+ includeList,
11216
+ pathnameList: Object.keys(pathnameDict)
11217
+ }).pathnameList.map(function (pathname) {
11218
+ return pathnameDict[pathname];
10829
11219
  });
10830
11220
  return data;
10831
11221
  }));
@@ -10834,7 +11224,7 @@ function sentinel() {}
10834
11224
 
10835
11225
  v8CoverageObj = v8CoverageListMerge(v8CoverageObj);
10836
11226
 
10837
- // debug v8CoverageObj.
11227
+ // Debug v8CoverageObj.
10838
11228
 
10839
11229
  await fsWriteFileWithParents(
10840
11230
  coverageDir + "v8_coverage_merged.json",
@@ -10843,7 +11233,7 @@ function sentinel() {}
10843
11233
 
10844
11234
  // 3. Create html-coverage-reports in <coverageDir>.
10845
11235
 
10846
- fileDict = {};
11236
+ fileDict = Object.create(null);
10847
11237
  await Promise.all(v8CoverageObj.result.map(async function ({
10848
11238
  functions,
10849
11239
  url: pathname
@@ -10974,6 +11364,7 @@ jslint_export = Object.freeze(Object.assign(jslint, {
10974
11364
  assertOrThrow,
10975
11365
  debugInline,
10976
11366
  fsWriteFileWithParents,
11367
+ globExclude,
10977
11368
  htmlEscape,
10978
11369
  jslint,
10979
11370
  jslint_apidoc,