sqlmath 2021.11.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.1-beta";
171
+ let jslint_edition = "v2022.6.1-beta";
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;
@@ -202,11 +293,12 @@ function assertJsonEqual(aa, bb, message) {
202
293
 
203
294
  // This function will assert JSON.stringify(<aa>) === JSON.stringify(<bb>).
204
295
 
205
- aa = JSON.stringify(objectDeepCopyWithKeysSorted(aa));
206
- bb = JSON.stringify(objectDeepCopyWithKeysSorted(bb));
296
+ aa = JSON.stringify(objectDeepCopyWithKeysSorted(aa), undefined, 1);
297
+ bb = JSON.stringify(objectDeepCopyWithKeysSorted(bb), undefined, 1);
207
298
  if (aa !== bb) {
208
299
  throw new Error(
209
- JSON.stringify(aa) + " !== " + JSON.stringify(bb) + (
300
+ "\n" + aa + "\n!==\n" + bb
301
+ + (
210
302
  typeof message === "string"
211
303
  ? " - " + message
212
304
  : message
@@ -264,6 +356,224 @@ async function fsWriteFileWithParents(pathname, data) {
264
356
  console.error("wrote file " + pathname);
265
357
  }
266
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
+
267
577
  function htmlEscape(str) {
268
578
 
269
579
  // This function will make <str> html-safe by escaping & < >.
@@ -304,10 +614,7 @@ function jslint(
304
614
  let import_list = []; // The array collecting all import-from strings.
305
615
  let line_list = String( // The array containing source lines.
306
616
  "\n" + source
307
- ).split(
308
- // rx_crlf
309
- /\n|\r\n?/
310
- ).map(function (line_source) {
617
+ ).split(jslint_rgx_crlf).map(function (line_source) {
311
618
  return {
312
619
  line_source
313
620
  };
@@ -352,8 +659,6 @@ function jslint(
352
659
  }
353
660
 
354
661
  function is_equal(aa, bb) {
355
- let aa_value;
356
- let bb_value;
357
662
 
358
663
  // test_cause:
359
664
  // ["0&&0", "is_equal", "", "", 0]
@@ -374,6 +679,7 @@ function jslint(
374
679
 
375
680
  // test_cause:
376
681
  // ["`${0}`&&`${0}`", "is_equal", "recurse_isArray", "", 0]
682
+ // ["`${0}`&&`${1}`", "is_equal", "recurse_isArray", "", 0]
377
683
 
378
684
  test_cause("recurse_isArray");
379
685
  return is_equal(value, bb[index]);
@@ -387,21 +693,19 @@ function jslint(
387
693
  // }
388
694
 
389
695
  jslint_assert(!Array.isArray(bb), `Expected !Array.isArray(bb).`);
390
- if (aa.id === "(number)" && bb.id === "(number)") {
696
+ switch (aa.id === bb.id && aa.id) {
697
+ case "(number)":
698
+ case "(string)":
391
699
  return aa.value === bb.value;
392
- }
393
- if (aa.id === "(string)") {
394
- aa_value = aa.value;
395
- } else if (aa.id === "`" && aa.constant) {
396
- aa_value = aa.value[0];
397
- }
398
- if (bb.id === "(string)") {
399
- bb_value = bb.value;
400
- } else if (bb.id === "`" && bb.constant) {
401
- bb_value = bb.value[0];
402
- }
403
- if (typeof aa_value === "string") {
404
- 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;
405
709
  }
406
710
  if (is_weird(aa) || is_weird(bb)) {
407
711
 
@@ -678,8 +982,17 @@ function jslint(
678
982
  `Expected 'Object.freeze('. All export values should be frozen.`
679
983
  );
680
984
  break;
681
- case "function_in_loop":
682
- mm = `Don't create functions within a loop.`;
985
+
986
+ // PR-378 - Relax warning "function_in_loop".
987
+ //
988
+ // case "function_in_loop":
989
+ // mm = `Don't create functions within a loop.`;
990
+ // break;
991
+
992
+ // PR-390 - Add numeric-separator check.
993
+
994
+ case "illegal_num_separator":
995
+ mm = `Illegal numeric separator '_' at column ${column}.`;
683
996
  break;
684
997
  case "infix_in":
685
998
  mm = (
@@ -833,6 +1146,15 @@ function jslint(
833
1146
  case "use_double":
834
1147
  mm = `Use double quotes, not single quotes.`;
835
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;
836
1158
  case "use_open":
837
1159
  mm = (
838
1160
  `Wrap a ternary expression in parens,`
@@ -866,6 +1188,12 @@ function jslint(
866
1188
  case "wrap_condition":
867
1189
  mm = `Wrap the condition in parens.`;
868
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;
869
1197
  case "wrap_immediate":
870
1198
  mm = (
871
1199
  `Wrap an immediate function invocation in parentheses to assist`
@@ -873,9 +1201,6 @@ function jslint(
873
1201
  + ` result of a function, and not the function itself.`
874
1202
  );
875
1203
  break;
876
- case "wrap_parameter":
877
- mm = `Wrap the parameter in parens.`;
878
- break;
879
1204
  case "wrap_regexp":
880
1205
  mm = `Wrap this regexp in parens to avoid confusion.`;
881
1206
  break;
@@ -1437,6 +1762,7 @@ async function jslint_cli({
1437
1762
  console_error,
1438
1763
  console_log,
1439
1764
  file,
1765
+ import_meta_url,
1440
1766
  mode_cli,
1441
1767
  mode_noop,
1442
1768
  option,
@@ -1451,8 +1777,8 @@ async function jslint_cli({
1451
1777
  let command;
1452
1778
  let data;
1453
1779
  let exit_code = 0;
1454
- let mode_plugin_vim;
1455
1780
  let mode_report;
1781
+ let mode_wrapper_vim;
1456
1782
  let result;
1457
1783
 
1458
1784
  function jslint_from_file({
@@ -1525,7 +1851,7 @@ async function jslint_cli({
1525
1851
  if (result_from_file.warnings.length > 0) {
1526
1852
  exit_code = 1;
1527
1853
  console_error(
1528
- mode_plugin_vim
1854
+ mode_wrapper_vim
1529
1855
 
1530
1856
  // PR-349 - Print warnings in format readable by vim.
1531
1857
 
@@ -1605,11 +1931,26 @@ async function jslint_cli({
1605
1931
  return count;
1606
1932
  }
1607
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
+
1608
1950
  // Feature-detect nodejs.
1609
1951
 
1610
1952
  if (!(
1611
- typeof process === "object"
1612
- && process
1953
+ (typeof process === "object" && process)
1613
1954
  && process.versions
1614
1955
  && typeof process.versions.node === "string"
1615
1956
  && !mode_noop
@@ -1635,7 +1976,7 @@ async function jslint_cli({
1635
1976
  ).test(process_argv[1])
1636
1977
  || mode_cli
1637
1978
  )
1638
- && moduleUrl.fileURLToPath(jslint_import_meta_url)
1979
+ && moduleUrl.fileURLToPath(import_meta_url)
1639
1980
  === modulePath.resolve(process_argv[1])
1640
1981
  )
1641
1982
  && !mode_cli
@@ -1658,17 +1999,17 @@ async function jslint_cli({
1658
1999
  }));
1659
2000
  return;
1660
2001
 
1661
- // COMMIT-b26d6df2 - Add command jslint_plugin_vim.
2002
+ // PR-363 - Add command jslint_report.
1662
2003
 
1663
- case "jslint_plugin_vim":
1664
- mode_plugin_vim = true;
2004
+ case "jslint_report":
2005
+ mode_report = command[1];
1665
2006
  process_argv = process_argv.slice(1);
1666
2007
  break;
1667
2008
 
1668
- // PR-363 - Add command jslint_report.
2009
+ // COMMIT-b26d6df2 - Add command jslint_wrapper_vim.
1669
2010
 
1670
- case "jslint_report":
1671
- mode_report = command[1];
2011
+ case "jslint_wrapper_vim":
2012
+ mode_wrapper_vim = true;
1672
2013
  process_argv = process_argv.slice(1);
1673
2014
  break;
1674
2015
 
@@ -1676,6 +2017,7 @@ async function jslint_cli({
1676
2017
 
1677
2018
  case "v8_coverage_report":
1678
2019
  await v8CoverageReportCreate({
2020
+ consoleError: console_error,
1679
2021
  coverageDir: command[1],
1680
2022
  processArgv: process_argv.slice(3)
1681
2023
  });
@@ -1684,9 +2026,9 @@ async function jslint_cli({
1684
2026
 
1685
2027
  // PR-349 - Detect cli-option --mode-vim-plugin.
1686
2028
 
1687
- mode_plugin_vim = (
2029
+ mode_wrapper_vim = (
1688
2030
  process_argv.slice(2).indexOf("--mode-vim-plugin") >= 0
1689
- || mode_plugin_vim
2031
+ || mode_wrapper_vim
1690
2032
  );
1691
2033
 
1692
2034
  // Normalize file relative to process.cwd().
@@ -1780,7 +2122,9 @@ async function jslint_cli({
1780
2122
  option
1781
2123
  });
1782
2124
  if (mode_report) {
1783
- 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);
1784
2128
  }
1785
2129
  process_exit(exit_code);
1786
2130
  return exit_code;
@@ -1822,37 +2166,16 @@ function jslint_phase2_lex(state) {
1822
2166
  let line_mega; // The starting line of megastring.
1823
2167
  let line_source = ""; // The remaining line source string.
1824
2168
  let line_whole = ""; // The whole line source string.
2169
+ let mode_digits_empty_string = 1;
2170
+ let mode_digits_numeric_separator = 2;
1825
2171
  let mode_directive = true; // true if directives are still allowed.
1826
2172
  let mode_mega = false; // true if currently parsing a megastring
1827
2173
  // ... literal.
1828
2174
  let mode_regexp; // true if regular expression literal seen on
1829
2175
  // ... this line.
1830
- let rx_token = new RegExp(
1831
- "^("
1832
- + "(\\s+)"
1833
- + "|([a-zA-Z_$][a-zA-Z0-9_$]*)"
1834
- + "|[(){}\\[\\],:;'\"~\\`]"
1835
- + "|\\?[?.]?"
1836
- + "|=(?:==?|>)?"
1837
- + "|\\.+"
1838
- + "|\\*[*\\/=]?"
1839
- + "|\\/[*\\/]?"
1840
- + "|\\+[=+]?"
1841
- + "|-[=\\-]?"
1842
- + "|[\\^%]=?"
1843
- + "|&[&=]?"
1844
- + "|\\"
1845
- + "|[|=]?"
1846
- + "|>{1,3}=?"
1847
- + "|<<?=?"
1848
- + "|!(?:!|==?)?"
1849
-
1850
- // PR-351 - Add BigInt support.
1851
-
1852
- + "|(0n?|[1-9][0-9]*n?)"
1853
- + ")"
1854
- + "(.*)$"
1855
- );
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.
1856
2179
  let snippet = ""; // A piece of string.
1857
2180
  let token_1; // The first token.
1858
2181
  let token_prv = token_global; // The previous token including
@@ -1934,7 +2257,7 @@ function jslint_phase2_lex(state) {
1934
2257
 
1935
2258
  warn_at("unexpected_a", line, column, char);
1936
2259
  }
1937
- if (read_digits("x") > 5) {
2260
+ if (read_digits("x", undefined) > 5) {
1938
2261
 
1939
2262
  // test_cause:
1940
2263
  // ["\"\\u{123456}\"", "char_after_escape", "too_many_digits", "", 11]
@@ -1951,7 +2274,7 @@ function jslint_phase2_lex(state) {
1951
2274
  return char_after();
1952
2275
  }
1953
2276
  char_before();
1954
- if (read_digits("x", true) < 4) {
2277
+ if (read_digits("x", mode_digits_empty_string) < 4) {
1955
2278
 
1956
2279
  // test_cause:
1957
2280
  // ["\"\\u0\"", "char_after_escape", "expected_four_digits", "", 5]
@@ -1986,6 +2309,26 @@ function jslint_phase2_lex(state) {
1986
2309
  return char;
1987
2310
  }
1988
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
+
1989
2332
  function lex_comment() {
1990
2333
  let body;
1991
2334
  let ii = 0;
@@ -2024,12 +2367,12 @@ function jslint_phase2_lex(state) {
2024
2367
  // Lex/loop through each line until "*/".
2025
2368
 
2026
2369
  while (true) {
2027
- // rx_star_slash
2370
+ // jslint_rgx_star_slash
2028
2371
  ii = line_source.indexOf("*/");
2029
2372
  if (ii >= 0) {
2030
2373
  break;
2031
2374
  }
2032
- // rx_slash_star
2375
+ // jslint_rgx_slash_star
2033
2376
  ii = line_source.indexOf("/*");
2034
2377
  if (ii >= 0) {
2035
2378
 
@@ -2049,8 +2392,7 @@ function jslint_phase2_lex(state) {
2049
2392
  }
2050
2393
  }
2051
2394
  jj = line_source.slice(0, ii).search(
2052
- // rx_slash_star_or_slash
2053
- /\/\*|\/$/
2395
+ jslint_rgx_slash_star_or_slash
2054
2396
  );
2055
2397
  if (jj >= 0) {
2056
2398
 
@@ -2068,13 +2410,7 @@ function jslint_phase2_lex(state) {
2068
2410
 
2069
2411
  // Uncompleted work comment.
2070
2412
 
2071
- if (
2072
- !option_dict.devel
2073
- && (
2074
- // rx_todo
2075
- /\b(?:todo|TO\s?DO|HACK)\b/
2076
- ).test(snippet)
2077
- ) {
2413
+ if (!option_dict.devel && jslint_rgx_todo.test(snippet)) {
2078
2414
 
2079
2415
  // test_cause:
2080
2416
  // ["//todo", "lex_comment", "todo_comment", "(comment)", 1] //jslint-quiet
@@ -2086,10 +2422,7 @@ function jslint_phase2_lex(state) {
2086
2422
 
2087
2423
  [
2088
2424
  the_comment.directive, body
2089
- ] = Array.from(snippet.match(
2090
- // rx_directive
2091
- /^(jslint|property|global)\s+(.*)$/
2092
- ) || []).slice(1);
2425
+ ] = Array.from(snippet.match(jslint_rgx_directive) || []).slice(1);
2093
2426
  if (the_comment.directive === undefined) {
2094
2427
  return the_comment;
2095
2428
  }
@@ -2111,10 +2444,12 @@ function jslint_phase2_lex(state) {
2111
2444
  // Lex/loop through each directive in /*...*/
2112
2445
 
2113
2446
  ii = 0;
2114
- body.replace((
2115
- // rx_directive_part
2116
- /([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*|$/g
2117
- ), function (match0, key, val, jj) {
2447
+ body.replace(jslint_rgx_directive_part, function (
2448
+ match0,
2449
+ key,
2450
+ val,
2451
+ jj
2452
+ ) {
2118
2453
  if (ii !== jj) {
2119
2454
 
2120
2455
  // test_cause:
@@ -2188,13 +2523,7 @@ function jslint_phase2_lex(state) {
2188
2523
  // string.
2189
2524
 
2190
2525
  while (true) {
2191
- match = line_source.match(
2192
-
2193
- // Vim-hack - vim-editor has trouble parsing '`' in regexp
2194
-
2195
- // rx_mega
2196
- /[\u0060\\]|\$\{/
2197
- ) || {
2526
+ match = line_source.match(jslint_rgx_mega) || {
2198
2527
  "0": "",
2199
2528
  index: 0
2200
2529
  };
@@ -2271,12 +2600,16 @@ function jslint_phase2_lex(state) {
2271
2600
 
2272
2601
  function lex_number() {
2273
2602
  let prefix = snippet;
2603
+
2604
+ // PR-390 - Add numeric-separator check.
2605
+
2606
+ check_numeric_separator(prefix, column - prefix.length);
2274
2607
  char_after();
2275
2608
  switch (prefix === "0" && char) {
2276
2609
  case "b":
2277
2610
  case "o":
2278
2611
  case "x":
2279
- read_digits(char);
2612
+ read_digits(char, mode_digits_numeric_separator);
2280
2613
 
2281
2614
  // PR-351 - Ignore BigInt suffix 'n'.
2282
2615
 
@@ -2286,14 +2619,14 @@ function jslint_phase2_lex(state) {
2286
2619
  break;
2287
2620
  default:
2288
2621
  if (char === ".") {
2289
- read_digits("d");
2622
+ read_digits("d", mode_digits_numeric_separator);
2290
2623
  }
2291
2624
  if (char === "E" || char === "e") {
2292
2625
  char_after(char);
2293
2626
  if (char !== "+" && char !== "-") {
2294
2627
  char_before();
2295
2628
  }
2296
- read_digits("d");
2629
+ read_digits("d", mode_digits_numeric_separator);
2297
2630
  }
2298
2631
  }
2299
2632
 
@@ -2563,7 +2896,7 @@ function jslint_phase2_lex(state) {
2563
2896
  }
2564
2897
  break;
2565
2898
  case "{":
2566
- if (read_digits("d", true) === 0) {
2899
+ if (read_digits("d", mode_digits_empty_string) === 0) {
2567
2900
 
2568
2901
  // test_cause:
2569
2902
  // ["aa=/aa{/", "lex_regexp_group", "expected_a_before_b", ",", 8]
@@ -2576,7 +2909,7 @@ function jslint_phase2_lex(state) {
2576
2909
  // ["aa=/.{,/", "lex_regexp_group", "comma", "", 0]
2577
2910
 
2578
2911
  test_cause("comma");
2579
- read_digits("d", true);
2912
+ read_digits("d", mode_digits_empty_string);
2580
2913
  }
2581
2914
  if (char_after("}") === "?") {
2582
2915
 
@@ -2869,7 +3202,7 @@ function jslint_phase2_lex(state) {
2869
3202
  }
2870
3203
  }
2871
3204
  from = column;
2872
- match = line_source.match(rx_token);
3205
+ match = line_source.match(jslint_rgx_token);
2873
3206
 
2874
3207
  // match[1] token
2875
3208
  // match[2] whitespace
@@ -3189,7 +3522,7 @@ node --input-type=module --eval '
3189
3522
  // /\*jslint beta, node*\/
3190
3523
  import moduleHttps from "https";
3191
3524
  (async function () {
3192
- let dict = {};
3525
+ let dict = Object.create(null);
3193
3526
  let result = "";
3194
3527
  await new Promise(function (resolve) {
3195
3528
  moduleHttps.get((
@@ -3252,41 +3585,48 @@ import moduleHttps from "https";
3252
3585
  return true;
3253
3586
  }
3254
3587
 
3255
- function read_digits(base, quiet) {
3588
+ function read_digits(base, mode) {
3256
3589
  let digits = line_source.match(
3257
3590
  base === "b"
3258
- ? (
3259
- // rx_bits
3260
- /^[01]*/
3261
- )
3591
+ ? jslint_rgx_digits_bits
3262
3592
  : base === "o"
3263
- ? (
3264
- // rx_octals
3265
- /^[0-7]*/
3266
- )
3593
+ ? jslint_rgx_digits_octals
3267
3594
  : base === "x"
3268
- ? (
3269
- // rx_hexs
3270
- /^[0-9A-F]*/i
3271
- )
3272
- : (
3273
- // rx_digits
3274
- /^[0-9]*/
3275
- )
3595
+ ? jslint_rgx_digits_hexs
3596
+ : jslint_rgx_digits_decimals
3276
3597
  )[0];
3277
- let length = digits.length;
3278
- if (!quiet && length === 0) {
3598
+ if (
3599
+ (mode !== mode_digits_empty_string && digits.length === 0)
3600
+ || digits[0] === "_"
3601
+ ) {
3279
3602
 
3280
3603
  // test_cause:
3281
3604
  // ["0x", "read_digits", "expected_digits_after_a", "0x", 2]
3605
+ // ["0x_", "read_digits", "expected_digits_after_a", "0x", 2]
3282
3606
 
3283
3607
  warn_at("expected_digits_after_a", line, column, snippet);
3284
3608
  }
3285
- column += length;
3286
- 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);
3287
3627
  snippet += digits;
3288
3628
  char_after();
3289
- return length;
3629
+ return digits.length;
3290
3630
  }
3291
3631
 
3292
3632
  function read_line() {
@@ -3357,6 +3697,7 @@ import moduleHttps from "https";
3357
3697
  test_cause("line_disable");
3358
3698
  line_source = "";
3359
3699
  }
3700
+ // jslint_rgx_tab
3360
3701
  if (line_source.indexOf("\t") >= 0) {
3361
3702
  if (!option_dict.white) {
3362
3703
 
@@ -3365,10 +3706,7 @@ import moduleHttps from "https";
3365
3706
 
3366
3707
  warn_at("use_spaces", line, line_source.indexOf("\t") + 1);
3367
3708
  }
3368
- line_source = line_source.replace((
3369
- // rx_tab
3370
- /\t/g
3371
- ), " ");
3709
+ line_source = line_source.replace(jslint_rgx_tab, " ");
3372
3710
  }
3373
3711
  if (!option_dict.white && line_source.endsWith(" ")) {
3374
3712
 
@@ -3388,9 +3726,11 @@ import moduleHttps from "https";
3388
3726
  from,
3389
3727
  id,
3390
3728
  identifier: Boolean(identifier),
3729
+ is_fart: false,
3391
3730
  line,
3392
3731
  nr: token_list.length,
3393
- thru: column
3732
+ thru: column,
3733
+ value
3394
3734
  };
3395
3735
  token_list.push(the_token);
3396
3736
 
@@ -3400,12 +3740,6 @@ import moduleHttps from "https";
3400
3740
  mode_directive = false;
3401
3741
  }
3402
3742
 
3403
- // If the token is to have a value, give it one.
3404
-
3405
- if (value !== undefined) {
3406
- the_token.value = value;
3407
- }
3408
-
3409
3743
  // If this token is an identifier that touches a preceding number, or
3410
3744
  // a "/", comment, or regular expression literal that touches a preceding
3411
3745
  // comment or regular expression literal, then give a missing space warning.
@@ -3439,6 +3773,29 @@ import moduleHttps from "https";
3439
3773
  the_token.dot = true;
3440
3774
  }
3441
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
+
3442
3799
  // The previous token is used to detect adjacency problems.
3443
3800
 
3444
3801
  token_prv = the_token;
@@ -3523,9 +3880,6 @@ function jslint_phase3_parse(state) {
3523
3880
  let catchage = catch_stack[0]; // The current catch-block.
3524
3881
  let functionage = token_global; // The current function.
3525
3882
  let mode_var; // "var" if using var; "let" if using let.
3526
- let rx_identifier = (
3527
- /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/
3528
- );
3529
3883
  let token_ii = 0; // The number of the next token.
3530
3884
  let token_now = token_global; // The current token being examined in
3531
3885
  // ... the parse.
@@ -3542,7 +3896,7 @@ function jslint_phase3_parse(state) {
3542
3896
  anon = token_now.id;
3543
3897
  } else if (
3544
3898
  token_now.id === "(string)"
3545
- && rx_identifier.test(token_now.value)
3899
+ && jslint_rgx_identifier.test(token_now.value)
3546
3900
  ) {
3547
3901
  anon = token_now.value;
3548
3902
  }
@@ -3554,7 +3908,7 @@ function jslint_phase3_parse(state) {
3554
3908
  match === undefined
3555
3909
 
3556
3910
  // test_cause:
3557
- // ["()", "advance", "expected_a_b", "(end)", 1]
3911
+ // ["{0:0}", "advance", "expected_a_b", "0", 2]
3558
3912
 
3559
3913
  ? stop("expected_a_b", token_nxt, id, artifact())
3560
3914
 
@@ -3603,7 +3957,7 @@ function jslint_phase3_parse(state) {
3603
3957
  // other assignment operators can modify, but they cannot initialize.
3604
3958
 
3605
3959
  const the_symbol = symbol(id, 20);
3606
- the_symbol.led = function (left) {
3960
+ the_symbol.led_infix = function (left) {
3607
3961
  const the_token = token_now;
3608
3962
  let right;
3609
3963
  the_token.arity = "assignment";
@@ -3915,7 +4269,7 @@ function jslint_phase3_parse(state) {
3915
4269
 
3916
4270
  const the_symbol = symbol(id);
3917
4271
  the_symbol.constant = true;
3918
- the_symbol.nud = (
4272
+ the_symbol.nud_prefix = (
3919
4273
  typeof value === "function"
3920
4274
  ? value
3921
4275
  : function () {
@@ -4109,7 +4463,7 @@ function jslint_phase3_parse(state) {
4109
4463
  // Create an infix operator.
4110
4464
 
4111
4465
  const the_symbol = symbol(id, bp);
4112
- the_symbol.led = function (left) {
4466
+ the_symbol.led_infix = function (left) {
4113
4467
  const the_token = token_now;
4114
4468
  the_token.arity = "binary";
4115
4469
  if (f !== undefined) {
@@ -4167,12 +4521,12 @@ function jslint_phase3_parse(state) {
4167
4521
  return the_token;
4168
4522
  }
4169
4523
 
4170
- function infix_fart_unwrapped(left) {
4524
+ function infix_fart_unwrapped() {
4171
4525
 
4172
4526
  // test_cause:
4173
- // ["aa=>0", "infix_fart_unwrapped", "wrap_parameter", "aa", 1]
4527
+ // ["aa=>0", "infix_fart_unwrapped", "wrap_fart_parameter", "=>", 3]
4174
4528
 
4175
- return stop("wrap_parameter", left);
4529
+ return stop("wrap_fart_parameter", token_now);
4176
4530
  }
4177
4531
 
4178
4532
  function infix_grave(left) {
@@ -4192,7 +4546,7 @@ function jslint_phase3_parse(state) {
4192
4546
  let the_subscript = parse_expression(0);
4193
4547
  if (the_subscript.id === "(string)" || the_subscript.id === "`") {
4194
4548
  name = survey(the_subscript);
4195
- if (rx_identifier.test(name)) {
4549
+ if (jslint_rgx_identifier.test(name)) {
4196
4550
 
4197
4551
  // test_cause:
4198
4552
  // ["aa[`aa`]", "infix_lbracket", "subscript_a", "aa", 4]
@@ -4335,13 +4689,13 @@ function jslint_phase3_parse(state) {
4335
4689
  // Create a right associative infix operator.
4336
4690
 
4337
4691
  const the_symbol = symbol(id, bp);
4338
- the_symbol.led = function parse_infixr_led(left) {
4692
+ the_symbol.led_infix = function parse_infixr_led(left) {
4339
4693
  const the_token = token_now;
4340
4694
 
4341
4695
  // test_cause:
4342
- // ["0**0", "parse_infixr_led", "led", "", 0]
4696
+ // ["0**0", "parse_infixr_led", "led_infix", "", 0]
4343
4697
 
4344
- test_cause("led");
4698
+ test_cause("led_infix");
4345
4699
  the_token.arity = "binary";
4346
4700
  the_token.expression = [left, parse_expression(bp - 1)];
4347
4701
  return the_token;
@@ -4349,39 +4703,70 @@ function jslint_phase3_parse(state) {
4349
4703
  return the_symbol;
4350
4704
  }
4351
4705
 
4352
- function lookahead() {
4353
-
4354
- // Look ahead one token without advancing, skipping comments.
4355
-
4356
- let cadet;
4357
- let ii = token_ii;
4358
- while (true) {
4359
- cadet = token_list[ii];
4360
- if (cadet.id !== "(comment)") {
4361
- return cadet;
4362
- }
4363
- ii += 1;
4364
- }
4365
- }
4366
-
4367
4706
  function parse_expression(rbp, initial) {
4368
4707
 
4369
4708
  // This is the heart of JSLINT, the Pratt parser. In addition to parsing, it
4370
- // is looking for ad hoc lint patterns. We add .fud to Pratt's model, which is
4371
- // like .nud except that it is only used on the first token of a statement.
4372
- // Having .fud makes it much easier to define statement-oriented languages like
4373
- // 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.
4374
4713
  // They are elements of the parsing method called Top Down Operator Precedence.
4375
4714
 
4376
- // .nud Null denotation
4377
- // .fud First null denotation
4378
- // .led Left denotation
4379
- // lbp Left binding power
4380
- // rbp Right binding power
4381
-
4382
- // It processes a nud (variable, constant, prefix operator). It will then
4383
- // process leds (infix operators) until the bind powers cause it to stop. It
4384
- // 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
+ // }
4385
4770
 
4386
4771
  let left;
4387
4772
  let the_symbol;
@@ -4393,13 +4778,13 @@ function jslint_phase3_parse(state) {
4393
4778
  advance();
4394
4779
  }
4395
4780
  the_symbol = syntax_dict[token_now.id];
4396
- if (the_symbol !== undefined && the_symbol.nud !== undefined) {
4781
+ if (the_symbol !== undefined && the_symbol.nud_prefix !== undefined) {
4397
4782
 
4398
4783
  // test_cause:
4399
4784
  // ["0", "parse_expression", "symbol", "", 0]
4400
4785
 
4401
4786
  test_cause("symbol");
4402
- left = the_symbol.nud();
4787
+ left = the_symbol.nud_prefix();
4403
4788
  } else if (token_now.identifier) {
4404
4789
 
4405
4790
  // test_cause:
@@ -4424,32 +4809,38 @@ function jslint_phase3_parse(state) {
4424
4809
  the_symbol = syntax_dict[token_nxt.id];
4425
4810
  if (
4426
4811
  the_symbol === undefined
4427
- || the_symbol.led === undefined
4812
+ || the_symbol.led_infix === undefined
4428
4813
  || the_symbol.lbp <= rbp
4429
4814
  ) {
4430
4815
  break;
4431
4816
  }
4432
4817
  advance();
4433
- left = the_symbol.led(left);
4818
+ left = the_symbol.led_infix(left);
4434
4819
  }
4435
4820
  return left;
4436
4821
  }
4437
4822
 
4438
- function parse_fart(pl) {
4823
+ function parse_fart() {
4824
+ let parameters;
4825
+ let signature;
4439
4826
  let the_fart;
4827
+ [parameters, signature] = prefix_function_arg();
4440
4828
  advance("=>");
4441
4829
  the_fart = token_now;
4442
4830
  the_fart.arity = "binary";
4443
4831
  the_fart.name = "=>";
4444
4832
  the_fart.level = functionage.level + 1;
4445
4833
  function_list.push(the_fart);
4446
- if (functionage.loop > 0) {
4447
4834
 
4448
- // test_cause:
4449
- // ["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) {
4450
4838
 
4451
- warn("function_in_loop", the_fart);
4452
- }
4839
+ // // test_cause:
4840
+ // // ["while(0){aa.map(()=>0);}", "parse_fart", "function_in_loop", "=>", 19]
4841
+ //
4842
+ // warn("function_in_loop", the_fart);
4843
+ // }
4453
4844
 
4454
4845
  // Give the function properties storing its names and for observing the depth
4455
4846
  // of loops and switches.
@@ -4457,6 +4848,8 @@ function jslint_phase3_parse(state) {
4457
4848
  the_fart.context = empty();
4458
4849
  the_fart.finally = 0;
4459
4850
  the_fart.loop = 0;
4851
+ the_fart.parameters = parameters;
4852
+ the_fart.signature = signature;
4460
4853
  the_fart.switch = 0;
4461
4854
  the_fart.try = 0;
4462
4855
 
@@ -4464,23 +4857,42 @@ function jslint_phase3_parse(state) {
4464
4857
 
4465
4858
  function_stack.push(functionage);
4466
4859
  functionage = the_fart;
4467
- the_fart.parameters = pl[0];
4468
- the_fart.signature = pl[1];
4469
- 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.
4470
4866
 
4471
4867
  // test_cause:
4472
- // ["(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]
4870
+
4871
+ warn("use_function_not_fart", the_fart);
4473
4872
 
4474
- test_cause("parameter");
4475
- enroll(name, "parameter", true);
4873
+ // Recurse enroll_parameter().
4874
+
4875
+ name.names.forEach(enroll_parameter);
4876
+ }
4476
4877
  });
4477
4878
  if (token_nxt.id === "{") {
4478
4879
 
4479
4880
  // test_cause:
4480
- // ["()=>{}", "parse_fart", "expected_a_b", "=>", 3]
4881
+ // ["()=>{}", "parse_fart", "use_function_not_fart", "=>", 3]
4481
4882
 
4482
- warn("expected_a_b", the_fart, "function", "=>");
4883
+ warn("use_function_not_fart", the_fart);
4483
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, "=>");
4484
4896
  } else {
4485
4897
  the_fart.expression = parse_expression(0);
4486
4898
  }
@@ -4495,13 +4907,7 @@ function jslint_phase3_parse(state) {
4495
4907
  let negative;
4496
4908
  switch (token_nxt.id) {
4497
4909
  case "(number)":
4498
- if (!(
4499
-
4500
- // https://datatracker.ietf.org/doc/html/rfc7159#section-6
4501
- // number = [ minus ] int [ frac ] [ exp ]
4502
-
4503
- /^-?(?:0|[1-9]\d*?)(?:\.\d*?)?(?:[eE][+\-]?\d+?)?$/
4504
- ).test(token_nxt.value)) {
4910
+ if (!jslint_rgx_json_number.test(token_nxt.value)) {
4505
4911
 
4506
4912
  // test_cause:
4507
4913
  // ["[-.0]", "parse_json", "unexpected_a", ".", 3]
@@ -4703,7 +5109,7 @@ function jslint_phase3_parse(state) {
4703
5109
  the_symbol = syntax_dict[first.id];
4704
5110
  if (
4705
5111
  the_symbol !== undefined
4706
- && the_symbol.fud !== undefined
5112
+ && the_symbol.fud_stmt !== undefined
4707
5113
 
4708
5114
  // PR-318 - Bugfix - Fixes issues #316, #317 - dynamic-import().
4709
5115
 
@@ -4712,7 +5118,7 @@ function jslint_phase3_parse(state) {
4712
5118
  the_symbol.disrupt = false;
4713
5119
  the_symbol.statement = true;
4714
5120
  token_now.arity = "statement";
4715
- the_statement = the_symbol.fud();
5121
+ the_statement = the_symbol.fud_stmt();
4716
5122
  functionage.statement_prv = the_statement;
4717
5123
  } else {
4718
5124
 
@@ -4782,7 +5188,7 @@ function jslint_phase3_parse(state) {
4782
5188
  // Create one of the postassign operators.
4783
5189
 
4784
5190
  const the_symbol = symbol(id, 150);
4785
- the_symbol.led = function (left) {
5191
+ the_symbol.led_infix = function (left) {
4786
5192
  token_now.expression = left;
4787
5193
  token_now.arity = "postassign";
4788
5194
  check_mutation(token_now.expression);
@@ -4796,7 +5202,7 @@ function jslint_phase3_parse(state) {
4796
5202
  // Create one of the preassign operators.
4797
5203
 
4798
5204
  const the_symbol = symbol(id);
4799
- the_symbol.nud = function () {
5205
+ the_symbol.nud_prefix = function () {
4800
5206
  const the_token = token_now;
4801
5207
  the_token.arity = "preassign";
4802
5208
  the_token.expression = parse_expression(150);
@@ -4811,7 +5217,7 @@ function jslint_phase3_parse(state) {
4811
5217
  // Create a prefix operator.
4812
5218
 
4813
5219
  const the_symbol = symbol(id);
4814
- the_symbol.nud = function () {
5220
+ the_symbol.nud_prefix = function () {
4815
5221
  const the_token = token_now;
4816
5222
  the_token.arity = "unary";
4817
5223
  if (typeof f === "function") {
@@ -4932,18 +5338,20 @@ function jslint_phase3_parse(state) {
4932
5338
  // }
4933
5339
  // jslint_assert(!mode_mega, `Expected !mode_mega.`);
4934
5340
 
4935
- // Don't create functions in loops. It is inefficient, and it can lead to
4936
- // scoping errors.
4937
-
4938
- if (functionage.loop > 0) {
4939
-
4940
- // test_cause:
4941
- // ["
4942
- // while(0){aa.map(function(){});}
4943
- // ", "prefix_function", "function_in_loop", "function", 17]
4944
-
4945
- warn("function_in_loop", the_function);
4946
- }
5341
+ // PR-378 - Relax warning "function_in_loop".
5342
+ //
5343
+ // // Don't create functions in loops. It is inefficient, and it can lead to
5344
+ // // scoping errors.
5345
+ //
5346
+ // if (functionage.loop > 0) {
5347
+ //
5348
+ // // test_cause:
5349
+ // // ["
5350
+ // // while(0){aa.map(function(){});}
5351
+ // // ", "prefix_function", "function_in_loop", "function", 17]
5352
+ //
5353
+ // warn("function_in_loop", the_function);
5354
+ // }
4947
5355
 
4948
5356
  // Give the function properties for storing its names and for observing the
4949
5357
  // depth of loops and switches.
@@ -4991,6 +5399,9 @@ function jslint_phase3_parse(state) {
4991
5399
  if (name.identifier) {
4992
5400
  enroll(name, "parameter", false);
4993
5401
  } else {
5402
+
5403
+ // Recurse enroll_parameter().
5404
+
4994
5405
  name.names.forEach(enroll_parameter);
4995
5406
  }
4996
5407
  });
@@ -5451,25 +5862,14 @@ function jslint_phase3_parse(state) {
5451
5862
  }
5452
5863
 
5453
5864
  function prefix_lparen() {
5454
- const cadet = lookahead().id;
5455
- const the_paren = token_now;
5865
+ let the_paren = token_now;
5456
5866
  let the_value;
5457
5867
 
5458
- // We can distinguish between a parameter list for => and a wrapped expression
5459
- // with one token of lookahead.
5868
+ // PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart.
5460
5869
 
5461
- if (
5462
- token_nxt.id === ")"
5463
- || token_nxt.id === "..."
5464
- || (token_nxt.identifier && (cadet === "," || cadet === "="))
5465
- ) {
5466
-
5467
- // test_cause:
5468
- // ["()=>0", "prefix_lparen", "fart", "", 0]
5469
-
5470
- test_cause("fart");
5870
+ if (token_now.is_fart) {
5471
5871
  the_paren.free = false;
5472
- return parse_fart(prefix_function_arg());
5872
+ return parse_fart();
5473
5873
  }
5474
5874
 
5475
5875
  // test_cause:
@@ -5487,31 +5887,6 @@ function jslint_phase3_parse(state) {
5487
5887
  }
5488
5888
  the_value.wrapped = true;
5489
5889
  advance(")", the_paren);
5490
- if (token_nxt.id === "=>") {
5491
- if (the_value.arity !== "variable") {
5492
- if (the_value.id === "{" || the_value.id === "[") {
5493
-
5494
- // test_cause:
5495
- // ["([])=>0", "prefix_lparen", "expected_a_before_b", "(", 1]
5496
- // ["({})=>0", "prefix_lparen", "expected_a_before_b", "(", 1]
5497
-
5498
- warn("expected_a_before_b", the_paren, "function", "(");
5499
-
5500
- // test_cause:
5501
- // ["([])=>0", "prefix_lparen", "expected_a_b", "=>", 5]
5502
- // ["({})=>0", "prefix_lparen", "expected_a_b", "=>", 5]
5503
-
5504
- return stop("expected_a_b", token_nxt, "{", "=>");
5505
- }
5506
-
5507
- // test_cause:
5508
- // ["(0)=>0", "prefix_lparen", "expected_identifier_a", "0", 2]
5509
-
5510
- return stop("expected_identifier_a", the_value);
5511
- }
5512
- the_paren.expression = [the_value];
5513
- return parse_fart([the_paren.expression, "(" + the_value.id + ")"]);
5514
- }
5515
5890
  return the_value;
5516
5891
  }
5517
5892
 
@@ -5592,12 +5967,12 @@ function jslint_phase3_parse(state) {
5592
5967
  anon = "anonymous";
5593
5968
  }
5594
5969
 
5595
- function stmt(id, fud) {
5970
+ function stmt(id, fud_stmt) {
5596
5971
 
5597
5972
  // Create a statement.
5598
5973
 
5599
5974
  const the_symbol = symbol(id);
5600
- the_symbol.fud = fud;
5975
+ the_symbol.fud_stmt = fud_stmt;
5601
5976
  return the_symbol;
5602
5977
  }
5603
5978
 
@@ -6032,10 +6407,7 @@ function jslint_phase3_parse(state) {
6032
6407
  advance("from");
6033
6408
  advance("(string)");
6034
6409
  the_import.import = token_now;
6035
- if (!(
6036
- // rx_module
6037
- /^[a-zA-Z0-9_$:.@\-\/]+$/
6038
- ).test(token_now.value)) {
6410
+ if (!jslint_rgx_module.test(token_now.value)) {
6039
6411
 
6040
6412
  // test_cause:
6041
6413
  // ["import aa from \"!aa\"", "stmt_import", "bad_module_name_a", "!aa", 16]
@@ -6646,13 +7018,13 @@ function jslint_phase3_parse(state) {
6646
7018
 
6647
7019
  if (id === "(string)") {
6648
7020
  id = name.value;
6649
- if (!rx_identifier.test(id)) {
7021
+ if (!jslint_rgx_identifier.test(id)) {
6650
7022
  return id;
6651
7023
  }
6652
7024
  } else if (id === "`") {
6653
7025
  if (name.value.length === 1) {
6654
7026
  id = name.value[0].value;
6655
- if (!rx_identifier.test(id)) {
7027
+ if (!jslint_rgx_identifier.test(id)) {
6656
7028
  return id;
6657
7029
  }
6658
7030
  }
@@ -6685,10 +7057,7 @@ function jslint_phase3_parse(state) {
6685
7057
  } else if (
6686
7058
  !option_dict.name
6687
7059
  && name.identifier
6688
- && (
6689
- // rx_weird_property
6690
- /^_|\$|Sync$|_$/m
6691
- ).test(id)
7060
+ && jslint_rgx_weird_property.test(id)
6692
7061
  ) {
6693
7062
 
6694
7063
  // test_cause:
@@ -6726,7 +7095,7 @@ function jslint_phase3_parse(state) {
6726
7095
  // Create a ternary operator.
6727
7096
 
6728
7097
  const the_symbol = symbol(id1, 30);
6729
- the_symbol.led = function parse_ternary_led(left) {
7098
+ the_symbol.led_infix = function parse_ternary_led(left) {
6730
7099
  const the_token = token_now;
6731
7100
  let second;
6732
7101
  second = parse_expression(20);
@@ -7442,10 +7811,7 @@ function jslint_phase4_walk(state) {
7442
7811
  test_cause("cack");
7443
7812
  cack = !cack;
7444
7813
  }
7445
- if ((
7446
- // rx_cap
7447
- /^[A-Z]/
7448
- ).test(left.name.id) !== cack) {
7814
+ if (jslint_rgx_cap.test(left.name.id) !== cack) {
7449
7815
  if (the_new !== undefined) {
7450
7816
 
7451
7817
  // test_cause:
@@ -7622,6 +7988,11 @@ function jslint_phase4_walk(state) {
7622
7988
  || thing.expression[0].constant === true
7623
7989
  || is_equal(thing.expression[1], thing.expression[2])
7624
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
+
7625
7996
  warn("unexpected_a", thing);
7626
7997
  } else if (is_equal(thing.expression[0], thing.expression[1])) {
7627
7998
 
@@ -8841,6 +9212,7 @@ function jslint_report({
8841
9212
 
8842
9213
  // This function will create human-readable, html-report
8843
9214
  // for warnings, properties, and functions from jslint-result-object.
9215
+ //
8844
9216
  // Example usage:
8845
9217
  // let result = jslint("console.log('hello world')");
8846
9218
  // let html = jslint_report(result);
@@ -8873,7 +9245,6 @@ function jslint_report({
8873
9245
  );
8874
9246
  }
8875
9247
 
8876
- html += "<div class=\"JSLINT_\" id=\"JSLINT_REPORT_HTML\">\n";
8877
9248
  html += String(`
8878
9249
  <style class="JSLINT_REPORT_STYLE">
8879
9250
  /* jslint utility2:true */
@@ -9022,19 +9393,38 @@ pyNj+JctcQLXenBOCms46aMkenIx45WpXqxxVJQLz/vgpmAVa0fmDv6Pue9xVTBPfVxCUGfj\
9022
9393
  /7xoEqvL+2E8VOyCTuT/7j269Zy4jUtN+g4="
9023
9394
  ) format("woff2");
9024
9395
  }
9025
- *,
9026
- *:after,
9027
- *: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 {
9028
9414
  border: 0;
9029
9415
  box-sizing: border-box;
9030
9416
  margin: 0;
9031
9417
  padding: 0;
9032
9418
  }
9419
+ /* disable text inflation algorithm used on some smartphones and tablets */
9033
9420
  .JSLINT_ {
9034
9421
  -ms-text-size-adjust: none;
9035
9422
  -webkit-text-size-adjust: none;
9036
9423
  text-size-adjust: none;
9037
9424
  }
9425
+ .JSLINT_REPORT_ div {
9426
+ box-sizing: border-box;
9427
+ }
9038
9428
  /*csslint ignore:end*/
9039
9429
 
9040
9430
  /* css - jslint_report - font */
@@ -9068,7 +9458,7 @@ pyNj+JctcQLXenBOCms46aMkenIx45WpXqxxVJQLz/vgpmAVa0fmDv6Pue9xVTBPfVxCUGfj\
9068
9458
  }
9069
9459
 
9070
9460
  /* css - jslint_report - general */
9071
- body {
9461
+ .JSLINT_ {
9072
9462
  background: antiquewhite;
9073
9463
  }
9074
9464
  .JSLINT_ fieldset {
@@ -9314,6 +9704,9 @@ body {
9314
9704
  : "global"
9315
9705
  );
9316
9706
  if (global.length + froms.length + exports.length > 0) {
9707
+ if (functions.length === 0) {
9708
+ html += "<br>\n";
9709
+ }
9317
9710
  html += "<div class=\"level level0\">\n";
9318
9711
  html += detail(module, global);
9319
9712
  html += detail("import from", froms);
@@ -9402,7 +9795,6 @@ body {
9402
9795
  });
9403
9796
  html += "</div>\n";
9404
9797
  html += "</fieldset>\n";
9405
- html += "</div>\n";
9406
9798
  return html;
9407
9799
  }
9408
9800
 
@@ -9478,14 +9870,14 @@ function jstestIt(description, testFunction, mode) {
9478
9870
  }));
9479
9871
  }
9480
9872
 
9481
- function jstestOnExit(exitCode, processExit, countFailed) {
9873
+ function jstestOnExit(exitCode, mode) {
9482
9874
 
9483
9875
  // This function will on process-exit, print test-report
9484
9876
  // and exit with non-zero exit-code if any test failed.
9485
9877
 
9486
9878
  let message = (
9487
9879
  (
9488
- (jstestCountFailed || countFailed)
9880
+ (jstestCountFailed || mode === "testsFailed")
9489
9881
  ? "\n\u001b[31m"
9490
9882
  : "\n\u001b[32m"
9491
9883
  )
@@ -9493,11 +9885,10 @@ function jstestOnExit(exitCode, processExit, countFailed) {
9493
9885
  + " tests failed - " + jstestCountFailed + "\n"
9494
9886
  + "\u001b[39m"
9495
9887
  );
9496
- if (!processExit) {
9888
+ if (mode !== "testsFailed") {
9497
9889
  console.error(message);
9498
- processExit = process.exit;
9499
9890
  }
9500
- processExit(exitCode || jstestCountFailed);
9891
+ process.exitCode = exitCode || jstestCountFailed;
9501
9892
  return message;
9502
9893
  }
9503
9894
 
@@ -9563,7 +9954,7 @@ function objectDeepCopyWithKeysSorted(obj) {
9563
9954
 
9564
9955
  // Recursively deep-copy obj with keys sorted.
9565
9956
 
9566
- sorted = {};
9957
+ sorted = Object.create(null);
9567
9958
  Object.keys(obj).sort().forEach(function (key) {
9568
9959
  sorted[key] = objectDeepCopyWithKeysSorted(obj[key]);
9569
9960
  });
@@ -10219,11 +10610,11 @@ async function v8CoverageReportCreate({
10219
10610
  // 3. Create html-coverage-reports in <coverageDir>.
10220
10611
 
10221
10612
  let cwd;
10613
+ let excludeList = [];
10222
10614
  let exitCode = 0;
10223
10615
  let fileDict;
10224
- let fileExcludeList = [];
10225
- let fileIncludeList = [];
10226
- let fileIncludeNodeModules;
10616
+ let includeList = [];
10617
+ let modeIncludeNodeModules;
10227
10618
  let processArgElem;
10228
10619
  let promiseList = [];
10229
10620
  let v8CoverageObj;
@@ -10248,9 +10639,19 @@ async function v8CoverageReportCreate({
10248
10639
  <style>
10249
10640
  /* jslint utility2:true */
10250
10641
  /*csslint ignore:start*/
10251
- * {
10252
- box-sizing: border-box;
10253
- 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;
10254
10655
  }
10255
10656
  /*csslint ignore:end*/
10256
10657
 
@@ -10396,6 +10797,7 @@ body {
10396
10797
  }
10397
10798
  txtBorder = (
10398
10799
  "+" + "-".repeat(padPathname + 2) + "+"
10800
+ + "-".repeat(padLines + 2) + "+"
10399
10801
  + "-".repeat(padLines + 2) + "+\n"
10400
10802
  );
10401
10803
  txt = "";
@@ -10403,7 +10805,8 @@ body {
10403
10805
  txt += txtBorder;
10404
10806
  txt += (
10405
10807
  "| " + String("Files covered").padEnd(padPathname, " ") + " | "
10406
- + String("Lines").padStart(padLines, " ") + " |\n"
10808
+ + String("Lines").padStart(padLines, " ") + " | "
10809
+ + String("Remaining").padStart(padLines, " ") + " |\n"
10407
10810
  );
10408
10811
  txt += txtBorder;
10409
10812
  fileList.forEach(function ({
@@ -10483,7 +10886,8 @@ body {
10483
10886
  + String("./" + pathname).padEnd(padPathname, " ") + " | "
10484
10887
  + String(
10485
10888
  modeCoverageIgnoreFile + " " + coveragePct + " %"
10486
- ).padStart(padLines, " ") + " |\n"
10889
+ ).padStart(padLines, " ") + " | "
10890
+ + " ".repeat(padLines) + " |\n"
10487
10891
  );
10488
10892
  txt += (
10489
10893
  "| " + "*".repeat(
@@ -10491,6 +10895,9 @@ body {
10491
10895
  ).padEnd(padPathname, "_") + " | "
10492
10896
  + String(
10493
10897
  linesCovered + " / " + linesTotal
10898
+ ).padStart(padLines, " ") + " | "
10899
+ + String(
10900
+ (linesTotal - linesCovered) + " / " + linesTotal
10494
10901
  ).padStart(padLines, " ") + " |\n"
10495
10902
  );
10496
10903
  txt += txtBorder;
@@ -10654,21 +11061,6 @@ ${String(count || "-0").padStart(7, " ")}
10654
11061
  ), txt));
10655
11062
  }
10656
11063
 
10657
- function pathnameRelativeCwd(pathname) {
10658
-
10659
- // This function will if <pathname> is inside <cwd>,
10660
- // return it relative to <cwd>, else empty-string.
10661
-
10662
- pathname = modulePath.resolve(pathname).replace((
10663
- /\\/g
10664
- ), "/");
10665
- if (!pathname.startsWith(cwd)) {
10666
- return;
10667
- }
10668
- pathname = pathname.slice(cwd.length);
10669
- return pathname;
10670
- }
10671
-
10672
11064
  /*
10673
11065
  function sentinel() {}
10674
11066
  */
@@ -10700,28 +11092,28 @@ function sentinel() {}
10700
11092
  processArgElem[1] = processArgElem.slice(1).join("=");
10701
11093
  switch (processArgElem[0]) {
10702
11094
 
10703
- // PR-371 - add cli-option `--exclude=aa,bb`
11095
+ // PR-371
11096
+ // Add cli-option `--exclude=...`.
10704
11097
 
10705
11098
  case "--exclude":
10706
- fileExcludeList = fileExcludeList.concat(
10707
- processArgElem[1].split(",")
10708
- );
11099
+ excludeList.push(processArgElem[1]);
10709
11100
  break;
10710
11101
 
10711
- // PR-371 - add cli-option `--exclude-node-modules=false`
11102
+ // PR-371
11103
+ // Add cli-option `--include=...`
10712
11104
 
10713
- case "--exclude-node-modules":
10714
- fileIncludeNodeModules = (
10715
- /0|false|null|undefined/
10716
- ).test(processArgElem[1]);
11105
+ case "--include":
11106
+ includeList.push(processArgElem[1]);
10717
11107
  break;
10718
11108
 
10719
- // 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`.
10720
11112
 
10721
- case "--include":
10722
- fileIncludeList = fileIncludeList.concat(
10723
- processArgElem[1].split(",")
10724
- );
11113
+ case "--include-node-modules":
11114
+ modeIncludeNodeModules = !(
11115
+ /0|false|null|undefined/
11116
+ ).test(processArgElem[1]);
10725
11117
  break;
10726
11118
  }
10727
11119
  }
@@ -10775,9 +11167,11 @@ function sentinel() {}
10775
11167
  ).test(file);
10776
11168
  });
10777
11169
  v8CoverageObj = await Promise.all(v8CoverageObj.map(async function (file) {
10778
- 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");
10779
11173
  data = JSON.parse(data);
10780
- data.result = data.result.filter(function (scriptCov) {
11174
+ data.result.forEach(function (scriptCov) {
10781
11175
  let pathname = scriptCov.url;
10782
11176
 
10783
11177
  // Filter out internal coverages.
@@ -10788,38 +11182,40 @@ function sentinel() {}
10788
11182
 
10789
11183
  // Normalize pathname.
10790
11184
 
10791
- pathname = pathnameRelativeCwd(moduleUrl.fileURLToPath(pathname));
10792
- if (
11185
+ pathname = moduleUrl.fileURLToPath(pathname);
11186
+ pathname = modulePath.resolve(pathname).replace((
11187
+ /\\/g
11188
+ ), "/");
10793
11189
 
10794
11190
  // Filter files outside of cwd.
10795
11191
 
10796
- !pathname
10797
- || pathname.startsWith("[")
11192
+ if (pathname.indexOf("[") >= 0 || !pathname.startsWith(cwd)) {
11193
+ return;
11194
+ }
10798
11195
 
10799
- // PR-371 - Filter directory node_modules.
11196
+ // Normalize pathname relative to cwd.
10800
11197
 
10801
- || (
10802
- !fileIncludeNodeModules
10803
- && (
10804
- /(?:^|\/)node_modules\//m
10805
- ).test(pathname)
10806
- )
11198
+ pathname = pathname.slice(cwd.length);
11199
+ scriptCov.url = pathname;
11200
+ pathnameDict[pathname] = scriptCov;
11201
+ });
10807
11202
 
10808
- // PR-371 - Filter fileExcludeList.
11203
+ // PR-400
11204
+ // Filter directory `node_modules`.
10809
11205
 
10810
- || fileExcludeList.indexOf(pathname) >= 0
11206
+ if (!modeIncludeNodeModules) {
11207
+ excludeList.push("node_modules/");
11208
+ }
10811
11209
 
10812
- // PR-371 - Filter fileIncludeList.
11210
+ // PR-400
11211
+ // Filter files by glob-patterns in excludeList, includeList.
10813
11212
 
10814
- || (
10815
- fileIncludeList.length > 0
10816
- && fileIncludeList.indexOf(pathname) === -1
10817
- )
10818
- ) {
10819
- return;
10820
- }
10821
- scriptCov.url = pathname;
10822
- return true;
11213
+ data.result = globExclude({
11214
+ excludeList,
11215
+ includeList,
11216
+ pathnameList: Object.keys(pathnameDict)
11217
+ }).pathnameList.map(function (pathname) {
11218
+ return pathnameDict[pathname];
10823
11219
  });
10824
11220
  return data;
10825
11221
  }));
@@ -10828,7 +11224,7 @@ function sentinel() {}
10828
11224
 
10829
11225
  v8CoverageObj = v8CoverageListMerge(v8CoverageObj);
10830
11226
 
10831
- // debug v8CoverageObj.
11227
+ // Debug v8CoverageObj.
10832
11228
 
10833
11229
  await fsWriteFileWithParents(
10834
11230
  coverageDir + "v8_coverage_merged.json",
@@ -10837,7 +11233,7 @@ function sentinel() {}
10837
11233
 
10838
11234
  // 3. Create html-coverage-reports in <coverageDir>.
10839
11235
 
10840
- fileDict = {};
11236
+ fileDict = Object.create(null);
10841
11237
  await Promise.all(v8CoverageObj.result.map(async function ({
10842
11238
  functions,
10843
11239
  url: pathname
@@ -10968,6 +11364,7 @@ jslint_export = Object.freeze(Object.assign(jslint, {
10968
11364
  assertOrThrow,
10969
11365
  debugInline,
10970
11366
  fsWriteFileWithParents,
11367
+ globExclude,
10971
11368
  htmlEscape,
10972
11369
  jslint,
10973
11370
  jslint_apidoc,